Python学习之 函数

  函数学习

  函数

  函数也是对象_内存分析

  # 测试函数也是对象

  def test01():

  print('GSH')

  test01()

  c = test01

  c() # 同样是调test01()

  print(id(test01)) # 4458865728

  print(id(c)) # 4458865728 此时他们(在栈内存中)指向了堆内存中同一个对象 所以他们的id值是一样的

  print(type(c))# 函数类型

  变量的作用域(全局变量和局部变量)

  全局变量是指在函数和类定义之外声明的变量。作用域为定义的模块,从定义位置开始直到模块结束。

  全局变量降低了函数的通用性和可读性,尽量避免使用。

  全局变量一般做常量使用。

  函数内要改变全局变量的值,使用global声明一下。

  局部变量是指在函数体重(包含形式参数)声明的变量,函数调用完就消失了。

  局部变量的引用比全局变量快,优先考虑使用。

  如果全局变量和局部变量同名,则在函数内隐藏全局变量,只使用同名的局部变量。

  测试全局变量 局部变量

  a = 3 # 全局变量

  def test01():

  b = 4 # 局部变量 作用域仅限函数内部

  print(b*4)

  # print(a) # 这里不可以直接用 因为默认是局部变量 但是这个局部变量并没有定义

  global a # 如果要在函数内部改变全局变量的值,增加global关键字声明

  a = 300

  # print(a) # 300

  test01()

  test01()

  print(a) # 可以打印 a是全局变量 此时因为函数内部因为global引用了a 所以函数内部可以将外部的全局变量进行改变a 此时a变成300

  # print(b) #打印不出来 不在b的作用域内

  打印结果:

  16

  16

  300

  locals() and globals()

  a = 100

  def f1(a,b,c):

  c = 0

  print(a,b,c)

  print(locals()) # 打印输出的局部变量

  print('*'*20)

  print(globals()) # 打印输出的全局变量

  f1(2,3,4)

  输出:

  2 3 0

  {'a': 2, 'b': 3, 'c': 0}

  ********************

  {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x100ba92d0>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': '/Users/guosihan/PycharmProjects/MyTest/day_6/func_test3.py', '__cached__': None, 'a': 100, 'f1': }

  局部变量和全局变量效率测试

  局部变量的查询和访问速度比全局变量快,优先考虑使用,尤其是在循环的时候。

  在特别强调效率的地方或者循环次数多的地方,可以通过将全局变量转为局部变量提高运行速度。

  # 测试局变量和全局变量的效率:

  import time

  import math

  def test01():

  start = time.time()

  for i in range(1000000):

  math.sqrt(30)

  end = time.time()

  print('耗时1:',(end - start)) # 耗时1: 0.09889388084411621

  def test02():

  b = math.sqrt

  start = time.time()

  for i in range(1000000):

  b(30)

  end = time.time()

  print('耗时2:',(end - start)) # 耗时2: 0.05476093292236328

  test01()

  test02()

  参数的传递

  函数的参数传递实质上就是: 从实参到形参的赋值操作。

  所有的赋值操作都是’引用的赋值’。所以,Python中参数的传递都是’引用的传递’。不是’值传递’。

  对’可变对象‘ 直接作用于原对象的本身。可变对象有: 字典,列表,集合,自定义的对象。

  对’不可变对象’会产生一个新的’对象空间’,并用新的值填充这块。 不可变的对象有: 数字,字符串,元祖,function。

  操作: 参数传递: 传递可变对象的引用。

  # 传递可变对象 列表举例

  b = [10,20]

  # 本质是b和m同时操作同一个对象

  def f2(m):

  print('id(m):',id(m)) # b和m是同一个对象

  m.append(30) # 由于m是可变对象 不创建对象拷贝 直接修改这个对象

  print('id(m):',id(m))

  f2(b)

  print('b:',id(b))

  print(b)

  结果:

  id(m): 4315951584

  id(m): 4315951584

  b: 4330934752

  [10, 20, 30]

  传递不可变对象的引用:

  # 传递不可变对象的引用 会产生一个新的对象

  a = 100

  def f1(n):

  print('n:',id(n)) # 传递进来的是a对象的地址

  n = n + 200 # 由于a是不可变对象 因此创建的是新的对象n

  print('n:',id(n)) # n已经变成了新的对象

  print(n)

  f1(a)

  print('a:',id(a))

  print('a:',a)

  结果:

  n: 4325429472

  n: 4327458768

  300

  a: 4325429472 # 跟第一个一样

  a: 100 # 注意这里还是没有变化 还是原来的100

  总结: 显然,通过id值我们可以看到n和a一开始是同一个对象。给n赋值后,n是新对象。因为原来的n是不可以变化的。

  浅拷贝和深拷贝

  内置函数copy(浅拷贝),deepcopy(深拷贝)的学习。

  浅拷贝: 不拷贝子对象的内容,只是拷贝子对象的引用。

  深拷贝: 会连子对象的内存也全部拷贝一份,对子对象的修改不会影响原对象。

  import copy

  # 浅拷贝

  def testcopy():

  a = [10,20,[5,6]]

  b = copy.copy(a)

  print('a:',a)

  print('b:',b)

  b.append(30)

  b[2].append(7)

  print('浅拷贝......')

  print('a:',a)

  print('b:',b)

  # 深拷贝

  def testDeepcopy():

  a = [10,20,[5,6]]

  b = copy.deepcopy(a)

  print('a:',a)

  print('b:',b)

  b.append(30)

  b[2].append(7)

  print('深拷贝......')

  print('a:',a)

  print('b:',b)

  testcopy()

  print('*********')

  testDeepcopy()

  输出结果:

  a: [10, 20, [5, 6]]

  b: [10, 20, [5, 6]]

  浅拷贝......

  a: [10, 20, [5, 6, 7]]

  b: [10, 20, [5, 6, 7], 30]

  *********

  a: [10, 20, [5, 6]]

  b: [10, 20, [5, 6]]

  深拷贝......

  a: [10, 20, [5, 6]]

  b: [10, 20, [5, 6, 7], 30]

  传递不可变对象时,不可变对象里面包含的事子对象是可变的。

  则方法内修改了这个可变对象 原对象也会发生变化。

  a = (10,20,[3,4])

  print('a:',id(a))

  def test01(m):

  print('m:',id(m))

  print(m)

  # a = 20 # 重新赋值

  m[2][0] = 888

  print('m:',id(m))

  print('函数内的a:',a)

  print('函数内的m',m)

  test01(a)

  print('函数外的a:',a)

  输出结果:

  a: 4345301824

  m: 4345301824

  (10, 20, [3, 4])

  m: 4345301824

  函数内的a: (10, 20, [888, 4])

  函数内的m (10, 20, [888, 4])

  函数外的a: (10, 20, [888, 4])

  参数的几种类型:

  位置参数

  参数为啥按顺序传递,需要个数和形参匹配,按位置传递参数,称为’位置参数’。

  默认值参数

  我们可以以为某些参数在函数内直接设置成默认值,这样这些参数在传递时就是可选的,称之为默认值参数,默认值参数放到位置参数的后面。

  def f1(a,b=1,c=9):

  print(a,b,c)

  f1(8,9) # 输出: 8 9 9

  命名参数

  我们可以按照形参的名称传递参数,称为"命名参数",也称为"关键字参数"。

  def f1(a,b,c):

  print(a,b,c)

  f1(8,9,10) # 此时位置必须对应的 此时是位置参数

  f1(c=10,b=2,a=4) # 根据名字来匹配 不一定非得按顺序来匹配

  可变参数

  *param(一个星号),将多个参数收集到一个’元祖’对象中。

  **param(两个星号),将多个参数收集到一个字典对象中。

  def f1(a,b,*c):

  print(a,b,c)

  f1(8,9,12,28)

  print('*********')

  def f2(a,b,**c):

  print(a,b,c)

  f2(8,9,name = 'GSH',age = 18)

  print('*********')

  def f3(a,b,*c,**d):

  print(a,b,c,d)

  f3(8,9,20,30,59,name = 'GSH',age = 18) # 没有参数名对应的是c,最后有参数名的是对应参数d

  输出:

  8 9 (12, 28)

  *********

  8 9 {'name': 'GSH', 'age': 18}

  *********

  8 9 (20, 30, 59) {'name': 'GSH', 'age': 18}

  强制命名参数

  带*号的"可变参数"后面增加新的参数,调用的时候必须是"强制命名参数"。

  def f4(*a,b,c):

  print(a,b,c)

  # 当调用f4时必须这样命名。

  f4(12,23,34,54,b = 2,c = 9)

  输出结果:

  (12, 23, 34, 54) 2 9

  lamnda 表达式和匿名函数

  lambda表达式可以用来声明匿名函数,lambda函数是一种简单的,在同一行中定义函数的方法。lambda函数实际生成了一个函数对象

  lambda表达式只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数的返回值。

  基本语法:

  lambda 参数1,参数2参数3... : <表达式>

  参数1 参数2 参数3位函数的参数.<表达式>相当于函数体。运算结果是: 表达式的运算结果。

  f = lambda a,b,c : a+b+c

  print(f)

  print(f(2,3,4))

  g = [lambda a:a*2,lambda b:b*3,lambda c:c*4]

  print(g[0](6),g[1](7),g[2](8)) # 0是列表序列,6是传进去的数字。

  # 解释:郑州妇科医院哪家好 http://www.sptdfk.com/

  def test01(a,b):

  return a+b

  #这里就是将函数看成一个对象传进h列表里

  h = [test01,test01]

  re = h[1](11,33)

  print(re) # 44

  输出:

  at 0x104c393b0>

  9

  12 21 32

  eval()函数

  # 测试eval()函数

  s = 'print("hello")'

  eval(s)

  a = 10

  b = 20

  c = eval('a+b')

  print(c) # 30

  dict1 = dict(a = 100,b = 200)

  d = eval('a+b')

  print(d) # 30

  e = eval('a+b',dict1)

  print(e) # 300

  递归函数

  # 测试递归函数的基本原理

  def test01(n):

  print('test01',n) # 执行语句1 递归语句前出现的语句是最先执行

  if n == 0:

  print('over') # 这里就是终止的条件

  else:

  test01(n-1) # 这里就是把第n步的值和第n-1步相关联

  print("test01***", n) # 执行语句2 递归语句后出现的语句是最后执行

  # test01() # 自己调自己 递归调用

  # RecursionError: maximum recursion depth exceeded while calling a Python object

  # print('********')

  def test02():

  print('test02')

  test01(4) # 第一个打开的是最后关闭的

  # 总结学习:

  # 就是自己调用自己的函数,在函数内部直接或间接的自己调用自己。

  # 1. 终止条件:表示递归函数什么时候结束。一般用于返回值,不再调用自己。

  # 2. 递归步骤:把第n步的值和第n-1步相关联。

  结果输出:

  test01 4

  test01 3

  test01 2

  test01 1

  test01 0

  over

  test01*** 0

  test01*** 1

  test01*** 2

  test01*** 3

  test01*** 4

  阶乘案例:

  # 使用递归函数 计算阶乘

  def factorial(n):

  if n == 1:

  return 1

  else:

  return factorial(n-1) * n

  re = factorial(5)

  print(re) # 120

posted @ 2020-04-02 15:37  tiana_Z  阅读(169)  评论(0编辑  收藏  举报