zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    luogu P4284 [SHOI2014]概率充电器 期望 概率 树形dp
    luogu P5161 WD与数列 SAM 线段树合并 启发式合并
    5.5 省选模拟赛 B Permutation 构造 贪心
    luogu P3761 [TJOI2017]城市 树的直径 bfs
    一本通 1783 矩阵填数 状压dp 容斥 计数
    CF R638 div2 F Phoenix and Memory 贪心 线段树 构造 Hall定理
    BSOJ 5445 -- 【2018雅礼】树 prufer序列 dp
    CF1037H Security 线段树合并 SAM
    c++11の顺序容器
    c++11の关联容器
  • 原文地址:https://www.cnblogs.com/djw12333/p/12620262.html
Copyright © 2011-2022 走看看