zoukankan      html  css  js  c++  java
  • 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在赋值之前就已经被引用了。

  • 相关阅读:
    Delphi公用函数单元
    Delphi XE5 for Android (十一)
    Delphi XE5 for Android (十)
    Delphi XE5 for Android (九)
    Delphi XE5 for Android (八)
    Delphi XE5 for Android (七)
    Delphi XE5 for Android (五)
    Delphi XE5 for Android (四)
    Delphi XE5 for Android (三)
    Delphi XE5 for Android (二)
  • 原文地址:https://www.cnblogs.com/hiyang/p/12634643.html
Copyright © 2011-2022 走看看