• python 基础篇 自定义函数


    多态

    我们可以看到,Python 不用考虑输入的数据类型,而是将其交给具体的代码去判断执行,同样的一个函数(比如这边的相加函数 my_sum()),可以同时应用在整型、列表、字符串等等的操作中。

    在编程语言中,我们把这种行为称为多态。这也是 Python 和其他语言,比如 Java、C 等很大的一个不同点。当然,Python 这种方便的特性,在实际使用中也会带来诸多问题。因此,必要时请你在开头加上数据的类型检查。

    def my_sum(a, b):
        if type(a) == type(b):
            if isinstance(a, (int, str, list)):
                return a + b
            else:
                raise Exception("input is not int/str/list")
        else:
            raise Exception("input type is not same")
    
    print(my_sum(3, 5))
    # 输出
    # 8
    
    print(my_sum([1, 2], [3, 4]))
    # 输出
    # [1, 2, 3, 4]
    
    print(my_sum('hello ', 'world'))
    # 输出
    # hello world
    
    print(my_sum([1, 2], 'hello'))
    # 输出
    # input type is not same
    

    函数嵌套

    Python 函数的另一大特性,是 Python 支持函数的嵌套。所谓的函数嵌套,就是指函数里面又有函数,比如:

    def f1():
        print('hello')
        def f2():
            print('world')
        f2()
    f1()
    
    # 输出
    hello
    world
    

    嵌套带来的好处

    1. 函数的嵌套能够保证内部函数的隐私。

      内部函数只能被外部函数所调用和访问,不会暴露在全局作用域,因此,如果你的函数内部有一些隐私数据(比如数据库的用户、密码等),不想暴露在外,那你就可以使用函数的的嵌套,将其封装在内部函数中,只通过外部函数来访问。比如:

      def connect_DB():
          def get_DB_configuration():
              ...
              return host, username, password
          conn = connector.connect(get_DB_configuration())
          return conn
      

      这里的函数 get_DB_configuration 是内部函数,它无法在 connect_DB() 函数以外被单独调用。也就是说,下面这样的外部直接调用是错误的:

      get_DB_configuration()
      
      # 输出
      NameError: name 'get_DB_configuration' is not defined
      
    2. 合理的使用函数嵌套,能够提高程序的运行效率。

      看下面这个例子:

      def factorial(input):
          # validation check
          if not isinstance(input, int):
              raise Exception('input must be an integer.')
          if input < 0:
              raise Exception('input must be greater or equal to 0' )
      
          def inner_factorial(input):
              if input <= 1:
                  return 1
              return input * inner_factorial(input-1)
          return inner_factorial(input)
      
      print(factorial(5))
      

      这里,我们使用递归的方式计算一个数的阶乘。因为在计算之前,需要检查输入是否合法,所以写成了函数嵌套的形式,这样一来,输入是否合法就只用检查一次。而如果我们不使用函数嵌套,那么每调用一次递归便会检查一次,这是没有必要的,也会降低程序的运行效率。

      实际工作中,如果你遇到相似的情况,输入检查不是很快,还会耗费一定的资源,那么运用函数的嵌套就十分必要。

    函数变量作用域

    1. 局部变量优先级高于全局变量

      如果遇到函数内部局部变量和全局变量同名的情况,那么在函数内部,局部变量会覆盖全局变量,比如下面这种:

      MIN_VALUE = 1
      MAX_VALUE = 10
      def validation_check(value):
          MIN_VALUE = 3
          ...
      

      这里MIN_VALUE=3

    2. 不能在函数内部随意改变全局变量的值

      MIN_VALUE = 1
      MAX_VALUE = 10
      def validation_check(value):
          ...
          MIN_VALUE += 1
          ...
      validation_check(5)
      

      如果运行这段代码,程序便会报错:

      UnboundLocalError: local variable 'MIN_VALUE' referenced before assignment
      

      这是因为,Python 的解释器会默认函数内部的变量为局部变量,但是又发现局部变量 MIN_VALUE 并没有声明,因此就无法执行相关操作。所以,如果我们一定要在函数内部改变全局变量的值,就必须加上 global 这个声明:

      MIN_VALUE = 1
      MAX_VALUE = 10
      def validation_check(value):
          global MIN_VALUE
          ...
          MIN_VALUE += 1
          ...
      validation_check(5)
      

      这里的 global 关键字,并不表示重新创建了一个全局变量 MIN_VALUE,而是告诉 Python 解释器,函数内部的变量MIN_VALUE,就是之前定义的全局变量,并不是新的全局变量,也不是局部变量。这样,程序就可以在函数内部访问全局变量,并修改它的值了.

    3. 对于嵌套函数来说,内部函数可以访问外部函数定义的变量,但是无法修改,若要修改,必须加上 nonlocal 这个关键字:

      def outer():
          x = "local"
          def inner():
              nonlocal x # nonlocal关键字表示这里的x就是外部函数outer定义的变量x
              x = 'nonlocal'
              print("inner:", x)
          inner()
          print("outer:", x)
      outer()
      # 输出
      inner: nonlocal
      outer: nonlocal
      

    闭包

    闭包(closure)其实和刚刚讲的嵌套函数类似,不同的是:

    • 在嵌套函数中外部函数返回的是一个具体的值
    • 闭包中外部函数返回的是一个函数,返回的函数通常赋于一个变量,这个变量可以在后面被继续执行调用。

    比如,我们想计算一个数的 n 次幂,用闭包可以写成下面的代码

    
    def nth_power(exponent):
        def exponent_of(base):
            return base ** exponent
        return exponent_of # 返回值是exponent_of函数
    
    square = nth_power(2) # 计算一个数的平方
    cube = nth_power(3) # 计算一个数的立方 
    square
    # 输出
    <function __main__.nth_power.<locals>.exponent(base)>
    
    cube
    # 输出
    <function __main__.nth_power.<locals>.exponent(base)>
    
    print(square(2))  # 计算2的平方
    print(cube(2)) # 计算2的立方
    # 输出
    4 # 2^2
    8 # 2^3
    

    需要注意的是,在执行完square = nth_power(2)cube = nth_power(3)后,外部函数 nth_power() 的参数 exponent,仍然会被内部函数 exponent_of() 记住。这样,之后我们调用 square(2) 或者 cube(2) 时,程序就能顺利地输出结果,而不会报错说参数 exponent 没有定义。

    闭包解决了函数运行基础变量问题,尤其这个函数需要被多次调用的时候。

    补充:UnboundLocalError

    参考博客

    函数虽然在不被调用的情况下不会执行,但是python解释器会做一些变量检测、或者类型检测,比如是不是有yield,如果有那么就会被标记为生成器,这个在编译成字节码的时候就已经确定了。

    import dis
    x = 1
    y = 2
    
    
    def foo():
        print(x)
        x = 2
        print(y)
    
    dis.dis(foo)
    
    # 直接调用 foo() 会报错
    # UnboundLocalError: local variable 'x' referenced before assignment
    
    # 输出
      7           0 LOAD_GLOBAL              0 (print)
                  2 LOAD_FAST                0 (x)
                  4 CALL_FUNCTION            1
                  6 POP_TOP
    
      8           8 LOAD_CONST               1 (2)
                 10 STORE_FAST               0 (x)
    
      9          12 LOAD_GLOBAL              0 (print)
                 14 LOAD_GLOBAL              1 (y)
                 16 CALL_FUNCTION            1
                 18 POP_TOP
                 20 LOAD_CONST               0 (None)
    

    因为python寻找变量的时候,会按照本地作用域、闭包、全局、内置这种顺序去查找,当看到x=2的时候,python解释器就知道函数体内部声明局部变量x,这是在编译的时候就已经确定,于是在print的时候也会从本地查找,但是print(x)语句在x=2的上面,这是在执行的时候才发现的,于是报了个错:提示局部变量x在赋值之前就已经被引用了。

  • 相关阅读:
    线上六个性能问题案例分享
    通达OA 前台任意用户登录漏洞复现
    CVE-2019-11043-Nginx PHP 远程代码执行
    CVE-2019-10758-Mongo-express-远程代码执行
    CVE-2017-7529-Nginx越界读取缓存漏洞
    add_header被覆盖 -配置错误
    目录穿越漏洞 -配置错误
    CRLF注入漏洞 -配置错误
    CVE-2019-12409-Apache Solr JMX服务远程代码执行
    CVE-2017-12149-JBoss 5.x/6.x 反序列化
  • 原文地址:https://www.cnblogs.com/hiyang/p/12634643.html
Copyright © 2020-2023  润新知