多态

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

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

  1. def my_sum(a, b):
  2. if type(a) == type(b):
  3. if isinstance(a, (int, str, list)):
  4. return a + b
  5. else:
  6. raise Exception("input is not int/str/list")
  7. else:
  8. raise Exception("input type is not same")
  9. print(my_sum(3, 5))
  10. # 输出
  11. # 8
  12. print(my_sum([1, 2], [3, 4]))
  13. # 输出
  14. # [1, 2, 3, 4]
  15. print(my_sum('hello ', 'world'))
  16. # 输出
  17. # hello world
  18. print(my_sum([1, 2], 'hello'))
  19. # 输出
  20. # input type is not same

函数嵌套

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

  1. def f1():
  2. print('hello')
  3. def f2():
  4. print('world')
  5. f2()
  6. f1()
  7. # 输出
  8. hello
  9. world

嵌套带来的好处

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

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

    1. def connect_DB():
    2. def get_DB_configuration():
    3. ...
    4. return host, username, password
    5. conn = connector.connect(get_DB_configuration())
    6. return conn

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

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

    看下面这个例子:

    1. def factorial(input):
    2. # validation check
    3. if not isinstance(input, int):
    4. raise Exception('input must be an integer.')
    5. if input < 0:
    6. raise Exception('input must be greater or equal to 0' )
    7. def inner_factorial(input):
    8. if input <= 1:
    9. return 1
    10. return input * inner_factorial(input-1)
    11. return inner_factorial(input)
    12. print(factorial(5))

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

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

函数变量作用域

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

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

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

    这里MIN_VALUE=3

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

    1. MIN_VALUE = 1
    2. MAX_VALUE = 10
    3. def validation_check(value):
    4. ...
    5. MIN_VALUE += 1
    6. ...
    7. validation_check(5)

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

    1. UnboundLocalError: local variable 'MIN_VALUE' referenced before assignment

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

    1. MIN_VALUE = 1
    2. MAX_VALUE = 10
    3. def validation_check(value):
    4. global MIN_VALUE
    5. ...
    6. MIN_VALUE += 1
    7. ...
    8. validation_check(5)

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

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

    1. def outer():
    2. x = "local"
    3. def inner():
    4. nonlocal x # nonlocal关键字表示这里的x就是外部函数outer定义的变量x
    5. x = 'nonlocal'
    6. print("inner:", x)
    7. inner()
    8. print("outer:", x)
    9. outer()
    10. # 输出
    11. inner: nonlocal
    12. outer: nonlocal

闭包

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

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

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


  1. def nth_power(exponent):
  2. def exponent_of(base):
  3. return base ** exponent
  4. return exponent_of # 返回值是exponent_of函数
  5. square = nth_power(2) # 计算一个数的平方
  6. cube = nth_power(3) # 计算一个数的立方
  7. square
  8. # 输出
  9. <function __main__.nth_power.<locals>.exponent(base)>
  10. cube
  11. # 输出
  12. <function __main__.nth_power.<locals>.exponent(base)>
  13. print(square(2)) # 计算2的平方
  14. print(cube(2)) # 计算2的立方
  15. # 输出
  16. 4 # 2^2
  17. 8 # 2^3

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

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

补充:UnboundLocalError

参考博客

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

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

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

python 基础篇 自定义函数的更多相关文章

  1. python 基础篇 11 函数进阶----装饰器

    11. 前⽅⾼能-装饰器初识本节主要内容:1. 函数名的运⽤, 第⼀类对象2. 闭包3. 装饰器初识 一:函数名的运用: 函数名是一个变量,但他是一个特殊变量,加上括号可以执行函数. ⼆. 闭包什么是 ...

  2. Python基础篇(初始函数)

    Python初始函数: 一.什么是函数 1.我们到目前为止, 已经可以完成一些软件的基础功能了. 那么我们来完成这样一个功 能: 约x: print("拿出手机") print(& ...

  3. python 基础篇 匿名函数

    匿名函数基础 首先,什么是匿名函数呢?以下是匿名函数的格式: lambda argument1, argument2,... argumentN : expression 我们可以看到,匿名函数的关键 ...

  4. python基础篇_003_函数

    python中的函数 1.函数的目的 .避免代码冗余 .增强可读性 2.函数的定义与调用 # 定义函数 使用关键字def """ 1.定义函数: def 函数名(): 函 ...

  5. Python基础(二)自定义函数

    1.判断字符串,内容是否为数字 我们用python:xlrd读Excel内容时,本来只是输入的整数字,经常读出来的是float类型 我们需要自动转成整型,意思就是说,读出来的和我们输入的一样,但是,我 ...

  6. python 基础篇 10 函数进阶

    本节主要内容:1. 函数参数--动态传参2. 名称空间, 局部名称空间, 全局名称空间, 作⽤域, 加载顺序.3. 函数的嵌套4. gloabal, nonlocal关键字 ⼀. 函数参数--动态传参 ...

  7. python 基础篇 09 函数初识

    <<<<<<<<<<<<<<<------------------------------函         ...

  8. Python基础篇(三)_函数及代码复用

    Python基础篇_函数及代码复用 函数的定义.使用: 函数的定义:通过保留字def实现. 定义形式:def <函数名>(<参数列表>): <函数体> return ...

  9. python基础篇(六)

    PYTHON基础篇(六) 正则模块re A:正则表达式和re模块案例 B:re模块的内置方法 时间模块time A:时间模块的三种表示方式 B:时间模块的相互转换 随机数模块random A:随机数模 ...

随机推荐

  1. TensorFlow系列专题(六):实战项目Mnist手写数据集识别

    欢迎大家关注我们的网站和系列教程:http://panchuang.net/ ,学习更多的机器学习.深度学习的知识! 目录: 导读 MNIST数据集 数据处理 单层隐藏层神经网络的实现 多层隐藏层神经 ...

  2. SQL Server Profiler常用功能

    最近因调研Linq to object 和Linq to Entity的数据组合查询问题,需要用到Sql Server Profiler检测在数据上执行的语句,在调试sql语句时,给了很大的帮助. 这 ...

  3. MATLAB 句柄绘图

    一.线句柄实例 >> h1=line([0:10],[0:10])%创建句柄值 h1 = Line (具有属性): Color: [0 0.4470 0.7410] LineStyle: ...

  4. docker-compose搭建redis哨兵集群

    头脑风暴 出于学习目的,您可以很轻松地在docker环境下运行redis的单个实例,但是如果您需要在生产环境中运行它,那么必须将Redis部署为HA(High Avaliable)模式. Redis ...

  5. Mybatis多表关联查询字段值覆盖问题

    一.错误展示 1.首先向大家展示多表关联查询的返回结果集 <resultMap id="specialdayAndWorktimeMap type="com.hierway. ...

  6. 从春节送祝福谈谈 IO 模型(二)

    上期结合程序员小猿用温奶器给孩子热奶的故事,把面试中常聊的“同步.异步与阻塞.非阻塞有啥区别”简单进行普及. 不过,恰逢春节即将到来,应个景,不妨就通过实现新春送祝福的需求,深入了解一下 Java I ...

  7. php的 '1' == 1, 返回true,到底是谁变成了谁?

    此过程为字符串的 '1' 隐含的编程了数字类型1,所以是true

  8. 少儿编程Scratch第一讲:Scratch完美的初体验

    素材及视频下载 链接:https://pan.baidu.com/s/1qX0T2B_zczcLaCCpiRrsnA提取码:xfp8 都说未来是人工智能.计算机程式控制的时代,如何让青少年接触计算机编 ...

  9. Redis对象——集合(Set)

    集合类型 (Set) 是一个无序并唯一的键值集合.它的存储顺序不会按照插入的先后顺序进行存储. 集合类型和列表类型的区别如下: 列表可以存储重复元素,集合只能存储非重复元素: 列表是按照元素的先后顺序 ...

  10. python基本知识点if、while、等等

    给予Python的相关知识点: 主要是对于基本语句的相关使用if else while for等先关的应用,以及步骤如下: 尝试编程如下:还有就是对于 import math import rando ...