1052 lines
30 KiB
Markdown
1052 lines
30 KiB
Markdown
|
# 09-Python函数详解
|
|||
|
|
|||
|
## 一、函数概念function
|
|||
|
|
|||
|
1. 函数是对实现某一功能的代码的封装
|
|||
|
2. 函数可以实现代码的复用,从而减少代码的重复编写
|
|||
|
3. 函数可以接受任何类型的输入作为其参数
|
|||
|
4. 函数可以通过关键字 return 可以返回任何类型的结果
|
|||
|
|
|||
|
没有使用函数式编程之前带来的问题:
|
|||
|
|
|||
|
1. 代码的组织结构不清晰,可读性差
|
|||
|
2. 实现重复的功能时,只能重复编写实现功能的代码,导致代码冗余,白白耗费精力
|
|||
|
3. 假如某一部分功能需要扩展或更新时,需要找出所有实现此功能的地方,一一修改,无法统一管理,加大维护难度
|
|||
|
|
|||
|
**函数分类**
|
|||
|
**内建函数**
|
|||
|
|
|||
|
builtins function
|
|||
|
|
|||
|
Python提供内建函数,无需事先定义,如len()、sum()、max()、print()
|
|||
|
|
|||
|
```python
|
|||
|
>> sum([1,1,1],20) #求和,只能接收列表和元组
|
|||
|
>> 23
|
|||
|
```
|
|||
|
|
|||
|
**自定义函数**
|
|||
|
用户自己创建的函数,被叫做用户自定义函数
|
|||
|
|
|||
|
## 二、函数定义和调用
|
|||
|
|
|||
|
**规则**
|
|||
|
1. 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号(),函数内容以冒号起始,并且缩进。
|
|||
|
圆括号中可以用于定义参数,任何传入参数和自变量必须放在圆括号中。
|
|||
|
2. 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
|
|||
|
return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
|
|||
|
|
|||
|
**语法**
|
|||
|
|
|||
|
```python
|
|||
|
def functionname( parameters ):
|
|||
|
"函数_文档字符串"
|
|||
|
function_suite
|
|||
|
return [expression]
|
|||
|
```
|
|||
|
|
|||
|
例
|
|||
|
|
|||
|
```python
|
|||
|
#!/usr/bin/python3.8
|
|||
|
def printme(str): # 定义函数
|
|||
|
"打印任何传入的字符串" # 文档字符串
|
|||
|
print(str)
|
|||
|
return
|
|||
|
|
|||
|
printme("hello world") # 调用函数,函数名加小括号
|
|||
|
```
|
|||
|
|
|||
|
**函数的调用**
|
|||
|
|
|||
|
1. 先找到名字
|
|||
|
2. 根据名字调用代码
|
|||
|
|
|||
|
**函数在定义阶段的动作**
|
|||
|
只检测语法,不执行代码,语法错误在函数定义阶段就会检测出来,而代码的逻辑错误只有在调用执行时才会知道
|
|||
|
|
|||
|
例
|
|||
|
|
|||
|
```python
|
|||
|
In [8]: def get_result(): # 函数定义阶段,没有报错,因为语法没有错误
|
|||
|
...: print(r - 1)
|
|||
|
...:
|
|||
|
|
|||
|
In [9]: get_result() # 运行函数报错
|
|||
|
NameError: name 'r' is not defined
|
|||
|
```
|
|||
|
|
|||
|
**定义函数的三种形式**
|
|||
|
**无参**
|
|||
|
|
|||
|
应用场景仅仅只是执行一些操作,比如与用户交互,打印
|
|||
|
|
|||
|
```python
|
|||
|
def myfunc():
|
|||
|
print("www.fklinux.com")
|
|||
|
```
|
|||
|
|
|||
|
**有参**
|
|||
|
|
|||
|
需要根据外部传进来的参数,才能执行相应的逻辑,比如统计时长,数学运算
|
|||
|
|
|||
|
```python
|
|||
|
def get_reslut(x, o , y):
|
|||
|
print(x)
|
|||
|
print(o)
|
|||
|
print(y)
|
|||
|
```
|
|||
|
|
|||
|
**空函数**
|
|||
|
|
|||
|
设计代码结构
|
|||
|
|
|||
|
```python
|
|||
|
def auth_user(user,password):
|
|||
|
'''
|
|||
|
auth function
|
|||
|
:param user: 用户名
|
|||
|
:param password: 密码
|
|||
|
:return: 认证结果
|
|||
|
'''
|
|||
|
pass
|
|||
|
|
|||
|
def get(filename):
|
|||
|
'''
|
|||
|
:param filename:
|
|||
|
:return:
|
|||
|
'''
|
|||
|
pass
|
|||
|
|
|||
|
def put(filename):
|
|||
|
'''
|
|||
|
:param filename:
|
|||
|
:return:
|
|||
|
'''
|
|||
|
```
|
|||
|
|
|||
|
**函数使用的原则**
|
|||
|
|
|||
|
必须得先定义,才可调用
|
|||
|
在使用函数时,一定要明确区分定义阶段和调用阶段
|
|||
|
在函数里面的任何代码都只是定义而已,即使函数体里面有其他函数也不会现在调用,只是定义要使用其他函数而已,只有在调用此函数时,这个函数内的代码才会执行。
|
|||
|
|
|||
|
```python
|
|||
|
# 定义阶段
|
|||
|
def foo():
|
|||
|
print('from foo') # 函数体里的东西在定义阶段实际上只是一堆字符串存到内存而已
|
|||
|
func()
|
|||
|
def func():
|
|||
|
print('from bar')
|
|||
|
|
|||
|
# 调用阶段
|
|||
|
foo()
|
|||
|
```
|
|||
|
|
|||
|
**有参函数和无参函数的调用对比**
|
|||
|
|
|||
|
```python
|
|||
|
定义阶段
|
|||
|
In [7]: def tell_tag(tag,n): # 有参数
|
|||
|
...: print(tag*n)
|
|||
|
...:
|
|||
|
...: def tell_msg(): # 无参数
|
|||
|
...: print(' hello world')
|
|||
|
...:
|
|||
|
调用阶段
|
|||
|
...: tell_tag('*',15)
|
|||
|
...: tell_msg()
|
|||
|
...: tell_tag('*',15)
|
|||
|
...:
|
|||
|
|
|||
|
***************
|
|||
|
|
|||
|
hello world
|
|||
|
|
|||
|
***************
|
|||
|
```
|
|||
|
|
|||
|
1、定义时无参,意味着调用时也无需传入参数
|
|||
|
2、定义时有参,意味着调用时则"必须"传入参数,默认参数虽然不用必须手动传参,但是也是传入了默认参数
|
|||
|
|
|||
|
**函数调用的方式**
|
|||
|
|
|||
|
1. 语句形式
|
|||
|
|
|||
|
```python
|
|||
|
foo()
|
|||
|
```
|
|||
|
|
|||
|
2. 表达式形式
|
|||
|
|
|||
|
```python
|
|||
|
3 * len('hello') # len() 是 python 的内置函数
|
|||
|
```
|
|||
|
|
|||
|
3. 做为另外一个函数的参数
|
|||
|
|
|||
|
```python
|
|||
|
range(len('hello'))
|
|||
|
range(5)
|
|||
|
```
|
|||
|
|
|||
|
4. 不带括号调用函数
|
|||
|
|
|||
|
```python
|
|||
|
In [1]: def hello(a,b):
|
|||
|
...: print(a + b)
|
|||
|
|
|||
|
In [2]: hello(1,2) # 函数调用,带括号表示告诉编译器"执行这个函数"
|
|||
|
|
|||
|
In [6]: c=hello # 不带括号表示把函数赋给另一个函数对象
|
|||
|
In [8]: c(3,4)
|
|||
|
7
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
|
|||
|
## 三、文档字符串
|
|||
|
|
|||
|
**定义**
|
|||
|
在函数体的第一行,可以使用一对三引号(''')或者(""")来定义文档字符串,文档字符串通常第一行以大写字母开头,以句号 (.)结束,第二行是空行,第三行开始是详细描述。强烈建议为你重要的函数写文档字符串都遵循此惯例。
|
|||
|
|
|||
|
**作用**
|
|||
|
文档字符串是使用Python过程中一个很重要的工具,他对程序文档很有帮助,使程序很容易理解。甚至当程序运行的时候,可以从一个函数中返回文档字符串。把函数当做一个对象来看,这更有助于我们的理解,就相当于获取一个对象的属性(__doc__).
|
|||
|
|
|||
|
函数文档字符串举例
|
|||
|
|
|||
|
```python
|
|||
|
# cat nester.py 这是一个模块文件
|
|||
|
#!/usr/bin/env python3.8
|
|||
|
'''这是模块文档字符串'''
|
|||
|
def print_list(name):
|
|||
|
'''这是函数文档字符串'''
|
|||
|
for each_item in name:
|
|||
|
if isinstance(each_item,list):
|
|||
|
print_list(each_item)
|
|||
|
else:
|
|||
|
print(each_item)
|
|||
|
|
|||
|
#python3.8
|
|||
|
>>> import nester
|
|||
|
>>> print(nester.print_list.__doc__)
|
|||
|
这是函数文档字符串
|
|||
|
>>> help(nester)
|
|||
|
Help on module nester:
|
|||
|
NAME
|
|||
|
nester - 这是模块文档字符串
|
|||
|
FILE
|
|||
|
/python/nester.py
|
|||
|
FUNCTIONS
|
|||
|
print_list(name)
|
|||
|
这是函数文档字符串
|
|||
|
(END)
|
|||
|
```
|
|||
|
|
|||
|
## 四、参数解析
|
|||
|
|
|||
|
### 1、形参和实参
|
|||
|
|
|||
|
**形参**
|
|||
|
|
|||
|
形式参数简称形参,是指**在定义函数时,定义的一个变量名**;
|
|||
|
下面的代码中,arg1、arg2、arg3就是形参
|
|||
|
|
|||
|
```python
|
|||
|
def foo(arg1,arg2,arg3)
|
|||
|
print('The first argument is ',arg1)
|
|||
|
print('The second argument is ',arg2)
|
|||
|
print('The third argument is ',arg3)
|
|||
|
```
|
|||
|
|
|||
|
**实参**
|
|||
|
|
|||
|
实际参数简称实参,是指**在调用函数时传入的实际的数据,是给形参赋值的**,可以理解为变量的值;函数调用时,将值绑定到变量名上,函数调用结束,解除绑定。
|
|||
|
下面的 1、2、3 就是实参。
|
|||
|
|
|||
|
```python
|
|||
|
foo(1,2,3)
|
|||
|
```
|
|||
|
|
|||
|
### 2、参数类型
|
|||
|
|
|||
|
必备参数
|
|||
|
关键字参数
|
|||
|
默认参数
|
|||
|
不定长参数
|
|||
|
|
|||
|
**必备参数**
|
|||
|
|
|||
|
对于函数来讲也叫位置参数,有位置上的要求,要一一对应
|
|||
|
必备参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
|
|||
|
比如调用printme()函数,你**必须传入一个参数**,不然会出现语法错误
|
|||
|
|
|||
|
```python
|
|||
|
#!/usr/bin/python3.8
|
|||
|
def printme( str ):
|
|||
|
print (str);
|
|||
|
return ;
|
|||
|
```
|
|||
|
|
|||
|
**关键字参数**
|
|||
|
在传入实参时以键值对的形式传入参数
|
|||
|
允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
|
|||
|
以下实例在函数 printme() 调用时使用参数名:
|
|||
|
|
|||
|
```python
|
|||
|
#!/usr/bin/python3.8
|
|||
|
def printme( str ):
|
|||
|
print(str);
|
|||
|
return;
|
|||
|
```
|
|||
|
|
|||
|
```python
|
|||
|
# 调用printme函数
|
|||
|
printme( str = "My string");
|
|||
|
输出结果:
|
|||
|
My string
|
|||
|
```
|
|||
|
|
|||
|
下例能将**关键字参数顺序不重要**展示得更清楚:
|
|||
|
|
|||
|
```python
|
|||
|
#!/usr/bin/python3.8
|
|||
|
def printinfo( name, age ):
|
|||
|
print ("Name: ", name);
|
|||
|
print ("Age ", age);
|
|||
|
return;
|
|||
|
```
|
|||
|
|
|||
|
```python
|
|||
|
# 调用printinfo函数
|
|||
|
printinfo( age=50, name="miki" )
|
|||
|
|
|||
|
输出结果:
|
|||
|
Name: miki
|
|||
|
Age 50
|
|||
|
```
|
|||
|
|
|||
|
**缺省参数**
|
|||
|
**调用函数时,缺省参数的值如果没有传入,则被认为是默认值**。
|
|||
|
下例会打印默认的age,如果age没有被传入:
|
|||
|
|
|||
|
```python
|
|||
|
#!/usr/bin/python3.8
|
|||
|
def printinfo( name, age = 35 ):
|
|||
|
print ("Name: ", name);
|
|||
|
print ("Age ", age);
|
|||
|
return;
|
|||
|
```
|
|||
|
|
|||
|
```python
|
|||
|
# 调用printinfo函数
|
|||
|
printinfo( age=50, name="miki" );
|
|||
|
printinfo( name="miki" )
|
|||
|
|
|||
|
输出结果:
|
|||
|
Name: miki
|
|||
|
Age 50
|
|||
|
Name: miki
|
|||
|
Age 35
|
|||
|
```
|
|||
|
|
|||
|
注:在函数定义时,**默认参数必须放在位置(必备)参数的后面**,比如:
|
|||
|
|
|||
|
```python
|
|||
|
In [49]: def printinfo( age=35,name ):
|
|||
|
...: print("Name: ", name);
|
|||
|
...: print("Age ", age);
|
|||
|
...: return;
|
|||
|
...:
|
|||
|
...:
|
|||
|
File "<ipython-input-49-ad599e8b1050>", line 1
|
|||
|
def printinfo( age=35,name ):
|
|||
|
^
|
|||
|
SyntaxError: non-default argument follows default argument
|
|||
|
```
|
|||
|
|
|||
|
**不定长参数**
|
|||
|
如果需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,也叫动态参数、万能参数,和上述2种参数不同,声明时不会命名。
|
|||
|
语法:
|
|||
|
|
|||
|
```python
|
|||
|
def functionname([formal_args,] *var_args_tuple ):
|
|||
|
function_suite
|
|||
|
return [expression]
|
|||
|
# 加了星号(*)的变量名会存放所有未命名的变量参数。
|
|||
|
```
|
|||
|
|
|||
|
例1:
|
|||
|
|
|||
|
```python
|
|||
|
#!/usr/bin/python3.8
|
|||
|
def printinfo( arg1, *vartuple ):
|
|||
|
print("输出: ")
|
|||
|
print(arg1)
|
|||
|
for var in vartuple:
|
|||
|
print(var)
|
|||
|
return
|
|||
|
|
|||
|
# 调用printinfo 函数
|
|||
|
printinfo( 10 )
|
|||
|
printinfo( 70, 60, 50 )
|
|||
|
|
|||
|
输出结果:
|
|||
|
输出:
|
|||
|
10
|
|||
|
输出:
|
|||
|
70
|
|||
|
60
|
|||
|
50
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
例2
|
|||
|
|
|||
|
```python
|
|||
|
#vim multi.py
|
|||
|
def multiarg(*args):
|
|||
|
for i in args:
|
|||
|
print(i)
|
|||
|
return
|
|||
|
|
|||
|
def multiargs(*args):
|
|||
|
print(args[2])
|
|||
|
return
|
|||
|
|
|||
|
print("第一个调用:")
|
|||
|
multiarg('hello','lili','tom','wing')
|
|||
|
print("第2个调用:")
|
|||
|
multiargs('hello','lili','tom','wing')
|
|||
|
|
|||
|
执行结果:
|
|||
|
第一个调用:
|
|||
|
hello
|
|||
|
lili
|
|||
|
tom
|
|||
|
wing
|
|||
|
第2个调用:
|
|||
|
tom
|
|||
|
```
|
|||
|
|
|||
|
`*args和**kwargs`
|
|||
|
|
|||
|
```python
|
|||
|
[wing@macserver ~]$ cat b.py
|
|||
|
#!/usr/bin/env python3.8
|
|||
|
def foo(*args,**kwargs):
|
|||
|
print ('args = ',args)
|
|||
|
print ('kwargs = ',kwargs)
|
|||
|
print ('-'*20)
|
|||
|
|
|||
|
if __name__ == '__main__':
|
|||
|
foo(1,2,3,4)
|
|||
|
foo(a=1,b=2,c=3)
|
|||
|
foo(1,2,3,4,a=1,b=2,c=3) # 同时使用*args和**kwargs,必须*args参数列在前
|
|||
|
foo('a',1,None,a=1,b='2',c=3)
|
|||
|
# *args是一个tuple,**kwargs表示关键字参数,是一个dict
|
|||
|
|
|||
|
执行结果:
|
|||
|
[wing@macserver ~]$ python b.py
|
|||
|
args = (1, 2, 3, 4)
|
|||
|
kwargs = {}
|
|||
|
--------------------
|
|||
|
args = ()
|
|||
|
kwargs = {'a': 1, 'c': 3, 'b': 2}
|
|||
|
--------------------
|
|||
|
args = (1, 2, 3, 4)
|
|||
|
kwargs = {'a': 1, 'c': 3, 'b': 2}
|
|||
|
--------------------
|
|||
|
args = ('a', 1, None)
|
|||
|
kwargs = {'a': 1, 'c': 3, 'b': '2'}
|
|||
|
--------------------
|
|||
|
```
|
|||
|
|
|||
|
**一些特殊的传参用法**
|
|||
|
|
|||
|
```python
|
|||
|
In [91]: def print_args(*args):
|
|||
|
...: print('传入的位置参数会包含在一个元组里:',args)
|
|||
|
...:
|
|||
|
```
|
|||
|
|
|||
|
情景 1 普通传参
|
|||
|
|
|||
|
```python
|
|||
|
In [92]: print_args(22,'192.168.1.10','wing')
|
|||
|
传入的位置参数会包含在一个元组里: (22, '192.168.1.10', 'wing')
|
|||
|
```
|
|||
|
|
|||
|
情景 2 传入列表
|
|||
|
|
|||
|
```python
|
|||
|
In [93]: li = [22,'192.168.1.10','wing']
|
|||
|
|
|||
|
In [94]: print_args(li)
|
|||
|
传入的位置参数会包含在一个元组里: ([22, '192.168.1.10', 'wing'],)
|
|||
|
# 上面的这种方式是,每个实参对象作为元组的一个元素
|
|||
|
|
|||
|
In [4]: print_args(*li)
|
|||
|
传入的位置参数会包含在一个元组里: (22, '192.168.1.10', 'wing')
|
|||
|
|
|||
|
In [5]: print_args(*'abc')
|
|||
|
传入的位置参数会包含在一个元组里: ('a', 'b', 'c')
|
|||
|
|
|||
|
# 这也就是传说中元组解包的原理
|
|||
|
# 把一个可迭代对象中的每个元素作为元组的每一个元素
|
|||
|
```
|
|||
|
|
|||
|
情景3 `**kwargs也有同样的用法`
|
|||
|
|
|||
|
```python
|
|||
|
In [6]: def print_kwargs(**kwargs):
|
|||
|
...: print("接收的关键字参数,会是一个字典:", kwargs)
|
|||
|
...:
|
|||
|
|
|||
|
In [7]: print_kwargs(name='wing')
|
|||
|
接收的关键字参数,会是一个字典: {'name': 'wing'}
|
|||
|
|
|||
|
In [8]: print_kwargs(ip='192.168.1.10', port=22)
|
|||
|
接收的关键字参数,会是一个字典: {'ip': '192.168.1.10', 'port': 22}
|
|||
|
|
|||
|
In [9]: dic = {'ip': '192.168.1.10', 'port': 22}
|
|||
|
|
|||
|
In [10]: print_kwargs(**dic)
|
|||
|
接收的关键字参数,会是一个字典: {'ip': '192.168.1.10', 'port': 22}
|
|||
|
|
|||
|
[root@workstation ~]# cat a.py
|
|||
|
li={1:"abc",1:"def"} # 在这里字典定义的时候key不能用数字,不然报错如下
|
|||
|
def foo(**arg01):
|
|||
|
print(arg01)
|
|||
|
foo(**li)
|
|||
|
|
|||
|
[root@workstation ~]# python3.8 a.py
|
|||
|
Traceback (most recent call last):
|
|||
|
File "a.py", line 5, in <module>
|
|||
|
foo(**li)
|
|||
|
TypeError: foo() keywords must be strings
|
|||
|
```
|
|||
|
|
|||
|
**三种形参在定义函数时的顺序如下**
|
|||
|
|
|||
|
```python
|
|||
|
In [108]: def position_arg(arg1, arg2, *args, k1='v1', **kwargs):
|
|||
|
...: pass
|
|||
|
...:
|
|||
|
```
|
|||
|
|
|||
|
**单星占位**
|
|||
|
|
|||
|
表示星后面的参数必须以关键字形式传参,默认关键字参数依然可用
|
|||
|
|
|||
|
星本身的位置不用传参
|
|||
|
|
|||
|
```python
|
|||
|
def hello(a,b,,*,c):
|
|||
|
print(a,b,c)
|
|||
|
|
|||
|
hello(1,2,c=5) #此位置的5不能直接传参
|
|||
|
-----------------------------------------
|
|||
|
def hello(a,b,*,c=5):
|
|||
|
print(a,b,c)
|
|||
|
|
|||
|
hello(1,2) #此位置c参数还是可以直接使用默认参数
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
|
|||
|
## 五、详解return
|
|||
|
|
|||
|
定义在函数体结尾,用来退出函数,选择性地向调用方返回一个表达式
|
|||
|
|
|||
|
**语法**
|
|||
|
return [表达式]
|
|||
|
|
|||
|
1、 在函数执行过程中,当在函数体内遇到 return关键字,函数就会立刻停止执行,并返回其返回值
|
|||
|
|
|||
|
```python
|
|||
|
def func():
|
|||
|
x = 100
|
|||
|
return # 函数到这里碰到return就结束了
|
|||
|
print('Ok')
|
|||
|
func() # 结果不会打印‘Ok‘
|
|||
|
```
|
|||
|
|
|||
|
2、return语句可以返回多个值,每个值用逗号隔开,即元组形式。
|
|||
|
3、return语句未写入 或 写入不带参数值的return语句 时则返回None
|
|||
|
|
|||
|
例:
|
|||
|
|
|||
|
```python
|
|||
|
# 想获取函数的return结果可以使用print()打印
|
|||
|
In [21]: print(fun())
|
|||
|
hello world
|
|||
|
None
|
|||
|
# 用print打印函数执行结果会包含返回值None
|
|||
|
```
|
|||
|
|
|||
|
例
|
|||
|
|
|||
|
```python
|
|||
|
#!/usr/bin/python3.8
|
|||
|
def sum( arg1, arg2 ):
|
|||
|
total = arg1 + arg2
|
|||
|
return total
|
|||
|
|
|||
|
# 调用sum函数
|
|||
|
print(sum( 10, 20 ))
|
|||
|
|
|||
|
# 输出结果
|
|||
|
30
|
|||
|
```
|
|||
|
|
|||
|
4、接收函数的返回值
|
|||
|
函数的返回值必须执行函数,此函数的返回值才会被创建并返回
|
|||
|
|
|||
|
注意:引用函数 返回的结果应该是返回值,而不是函数体打印的东西
|
|||
|
|
|||
|
```python
|
|||
|
ret = foo()
|
|||
|
# 使用一个变量来接收一个函数的返回值
|
|||
|
print("foo 函数的返回值是:", ret)
|
|||
|
```
|
|||
|
|
|||
|
5、当返回值为多个的时候,必须使用多个变量接收,实际上就是元组解包
|
|||
|
|
|||
|
```python
|
|||
|
def func():
|
|||
|
x = 100
|
|||
|
return x,1,3
|
|||
|
|
|||
|
a,b,c=func()
|
|||
|
print(a,b,c)
|
|||
|
```
|
|||
|
|
|||
|
## 六、变量作用域
|
|||
|
|
|||
|
一个程序所有的变量并不是在任何位置都可以访问的。访问权限取决于这个变量是在哪里赋值的。
|
|||
|
变量的作用域决定了在哪一部分程序你可以访问哪个特定的变量名称。
|
|||
|
调用函数时,所有在函数内声明的变量名称都将被加入到作用域中
|
|||
|
|
|||
|
1、全局变量和局部变量
|
|||
|
|
|||
|
定义在函数内部的变量拥有一个局部作用域
|
|||
|
定义在函数外的变量拥有全局作用域
|
|||
|
|
|||
|
局部变量只能在其被声明的函数内部访问
|
|||
|
全局变量可以在整个程序范围内访问
|
|||
|
|
|||
|
例
|
|||
|
|
|||
|
```python
|
|||
|
#!/usr/bin/env python3.8
|
|||
|
total = 0 # 这是一个全局变量
|
|||
|
def sum( arg1, arg2 ):
|
|||
|
globaltotal = arg1 + arg2 # total在这里是局部变量.
|
|||
|
print("函数内是局部变量 : ", total)
|
|||
|
return total;
|
|||
|
sum( 10, 20 );
|
|||
|
print("函数外是全局变量 : ", total)
|
|||
|
|
|||
|
# 调用sum函数
|
|||
|
sum( 10, 20 );
|
|||
|
print "函数外是全局变量 : ",total
|
|||
|
|
|||
|
输出结果:
|
|||
|
函数内是局部变量 : 30
|
|||
|
函数外是全局变量 : 0
|
|||
|
```
|
|||
|
|
|||
|
**命名空间和作用域**
|
|||
|
|
|||
|
命名空间:namespace
|
|||
|
是一个包含了变量名称(键)和它们各自相应的对象(值)的字典。
|
|||
|
|
|||
|
1. 一个Python表达式可以访问局部命名空间和全局命名空间里的变量。
|
|||
|
2. 如果一个局部变量和一个全局变量重名,则局部变量会覆盖全局变量。
|
|||
|
3. 每个函数都有自己的命名空间。类的方法的作用域规则和通常函数的一样。
|
|||
|
4. Python认为任何在函数内赋值的变量都是局部的。因此,如果要给全局变量在一个函数里赋值,必须使用global语句。
|
|||
|
|
|||
|
**global关键字**
|
|||
|
|
|||
|
global VarName表达式会告诉Python, VarName是一个全局变量,这样Python就不会在局部命名空间里寻找这个变量了。
|
|||
|
|
|||
|
例如,在全局命名空间里定义一个变量Money,再在函数内给变量Money赋值,然后Python会假定Money是一个局部变量。然而,并没有在访问前声明一个局部变量Money,结果就是会出现一个UnboundLocalError的错误。需要使用global语句生命使用全局变量Money。
|
|||
|
|
|||
|
```python
|
|||
|
#!/usr/bin/python3.8
|
|||
|
Money = 2000
|
|||
|
def AddMoney():
|
|||
|
# global Money
|
|||
|
Money = Money + 1
|
|||
|
return Money
|
|||
|
print(AddMoney())
|
|||
|
```
|
|||
|
|
|||
|
**globals()和locals()函数**
|
|||
|
根据调用地方的不同,globals()和locals()函数可被用来返回全局和局部命名空间里的名字。
|
|||
|
|
|||
|
在函数内部调用locals():
|
|||
|
返回的是所有能在该函数里访问的命名。
|
|||
|
|
|||
|
在函数内部调用globals():
|
|||
|
返回的是所有在该函数里能访问的全局名字。
|
|||
|
|
|||
|
两个函数的返回类型都是字典。所以名字们能用keys()函数摘取。
|
|||
|
|
|||
|
## 七、lambda表达式
|
|||
|
|
|||
|
Python 使用 lambda 来创建匿名函数。
|
|||
|
• 匿名是因为不需要以标准的方式来声明,比如说, 使用 def 语句。
|
|||
|
• 一个完整的 lambda"语句"代表了一个表达式,这个表达式的定义体必须和声明放在同一行。
|
|||
|
• lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
|
|||
|
• lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
|
|||
|
**语法**
|
|||
|
lambda [arg1 [,arg2,.....argn]]:expression
|
|||
|
参数是可选的,如果使用参数,参数通常也是表达式的一部分。
|
|||
|
例:
|
|||
|
|
|||
|
```python
|
|||
|
#!/usr/bin/python3.8
|
|||
|
sum = lambda arg1, arg2: arg1 + arg2
|
|||
|
|
|||
|
|
|||
|
# 调用sum函数
|
|||
|
print("相加后的值为 : ", sum( 10, 20 ))
|
|||
|
print("相加后的值为 : ", sum( 20, 20 ))
|
|||
|
|
|||
|
输出结果:
|
|||
|
相加后的值为 : 30
|
|||
|
相加后的值为 : 40
|
|||
|
```
|
|||
|
|
|||
|
**单行语句**
|
|||
|
|
|||
|
```python
|
|||
|
def true():
|
|||
|
return True
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
上面的函数没有带任何的参数并且总是返回 True。python 中单行函数可以和标题写在同一行
|
|||
|
|
|||
|
```python
|
|||
|
重写true()函数:
|
|||
|
def true(): return True
|
|||
|
```
|
|||
|
|
|||
|
使用 lambda 的等价表达式(没有参数,返回一个 True)为:
|
|||
|
|
|||
|
```python
|
|||
|
lambda :True
|
|||
|
```
|
|||
|
|
|||
|
```python
|
|||
|
In [4]: a = lambda x, y=2: x + y
|
|||
|
In [5]: a(5)
|
|||
|
7
|
|||
|
|
|||
|
In [6]: a(3,5)
|
|||
|
8
|
|||
|
```
|
|||
|
|
|||
|
```python
|
|||
|
In [2]: a=lambda *z:z
|
|||
|
In [3]: a(1,2,3,4)
|
|||
|
(1, 2, 3, 4)
|
|||
|
```
|
|||
|
|
|||
|
## 八、内建函数
|
|||
|
|
|||
|
Python按照对象是否可变,将类型分类为:
|
|||
|
|
|||
|
不可变类型:
|
|||
|
对象的内容不能够改变(not mutable),这些类型中主要有数值类型(整数,浮点数,复数),字符
|
|||
|
串类型,元组等
|
|||
|
|
|||
|
可变类型:
|
|||
|
对象的内容能够改变(mutable),主要有列表,字典
|
|||
|
|
|||
|
Python针对众多的类型,提供了众多内建函数来处理
|
|||
|
|
|||
|
**内建函数的查看**
|
|||
|
|
|||
|
```python
|
|||
|
>>> dir(__builtins__)
|
|||
|
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
|
|||
|
```
|
|||
|
|
|||
|
```python
|
|||
|
>>> import __builtin__
|
|||
|
>>> dir(__builtin__)
|
|||
|
```
|
|||
|
|
|||
|
**获取内建函数帮助:**
|
|||
|
help()
|
|||
|
获取帮助信息
|
|||
|
|
|||
|
其完整的一般使用形式为:
|
|||
|
help(module.class.function)
|
|||
|
|
|||
|
例子:
|
|||
|
In [3]: import sys
|
|||
|
In [4]: help(sys.getsizeof)
|
|||
|
|
|||
|
**函数使用例子**
|
|||
|
|
|||
|
需要对List、Dict进行排序,Python提供了两个方法对给定的List L进行排序:
|
|||
|
方法1.用List的成员函数sort进行排序,在本地进行排序,不返回副本
|
|||
|
方法2.用built-in函数sorted进行排序(从2.4开始),返回副本,原始输入不变
|
|||
|
--------------------------------sorted---------------------------------------
|
|||
|
\>>> help(sorted)
|
|||
|
Help on built-in function sorted in module __builtin__:
|
|||
|
|
|||
|
sorted(...)
|
|||
|
sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list
|
|||
|
---------------------------------sort----------------------------------------
|
|||
|
\>>> help(list.sort)
|
|||
|
Help on method_descriptor:
|
|||
|
|
|||
|
sort(...)
|
|||
|
L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*;
|
|||
|
cmp(x, y) -> -1, 0, 1
|
|||
|
\-----------------------------------------------------------------------------
|
|||
|
参数说明:
|
|||
|
|
|||
|
1. iterable:
|
|||
|
是可迭代类型;
|
|||
|
|
|||
|
2. cmp参数
|
|||
|
|
|||
|
cmp接受一个用于比较的函数,比较什么由key决定,拿整形举例,形式为:
|
|||
|
def f(a,b):
|
|||
|
return a-b
|
|||
|
如果排序的元素是其他类型的,如果a逻辑小于b,函数返回负数;a逻辑等于b,函数返回0;a逻辑大于
|
|||
|
b,函数返回正数就行了
|
|||
|
|
|||
|
3. key参数
|
|||
|
key用列表元素的某个属性和函数作为关键字,也是接受一个函数,不同的是,这个函数只接受一个元素,
|
|||
|
形式如下
|
|||
|
def f(a):
|
|||
|
return len(a)
|
|||
|
key接受的函数返回值,表示此元素的权值,sort将按照权值大小进行排序
|
|||
|
|
|||
|
4. reverse参数
|
|||
|
接受False 或者True 表示是否逆序
|
|||
|
|
|||
|
返回值:是一个经过排序的可迭代类型,与iterable一样。
|
|||
|
注:一般来说,cmp和key可以使用lambda表达式。
|
|||
|
|
|||
|
例子:
|
|||
|
**(1)按照元素长度排序**
|
|||
|
使用sorted排序:
|
|||
|
\#!/usr/bin/env python
|
|||
|
L = [{1:5,3:4},{1:3,6:3},{1:1,2:4,5:6},{1:9}]
|
|||
|
def f(x):
|
|||
|
return len(x)
|
|||
|
M=sorted(L,key=f)
|
|||
|
print M
|
|||
|
|
|||
|
输出:
|
|||
|
[{1: 9}, {1: 5, 3: 4}, {1: 3, 6: 3}, {1: 1, 2: 4, 5: 6}]
|
|||
|
|
|||
|
使用sort排序:
|
|||
|
\#!/usr/bin/env python
|
|||
|
L = [{1:5,3:4},{1:3,6:3},{1:1,2:4,5:6},{1:9}]
|
|||
|
def f(x):
|
|||
|
return len(x)
|
|||
|
L.sort(key=f)
|
|||
|
print L
|
|||
|
|
|||
|
**(2)按照每个字典元素里面key为1的元素的值排序**
|
|||
|
L = [{1:5,3:4},{1:3,6:3},{1:1,2:4,5:6},{1:9}]
|
|||
|
def f2(a,b):
|
|||
|
return a[1]-b[1]
|
|||
|
L.sort(cmp=f2)
|
|||
|
print L
|
|||
|
|
|||
|
**对由tuple组成的List排序**
|
|||
|
|
|||
|
由tuple组成的List:
|
|||
|
\>>> students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10),]
|
|||
|
|
|||
|
用key函数排序:返回由tuple组成的list
|
|||
|
\>>> sorted(students, key=lambda student : student[2]) **# sort by age**
|
|||
|
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
|
|||
|
|
|||
|
用cmp函数排序 :
|
|||
|
\>>> sorted(students, cmp=lambda x,y : cmp(x[2], y[2])) **# sort by age**
|
|||
|
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
|
|||
|
|
|||
|
用 operator 函数来加快速度:
|
|||
|
\>>> from operator import itemgetter, attrgetter
|
|||
|
\>>> sorted(students, key=itemgetter(2))
|
|||
|
|
|||
|
用 operator 函数进行多级排序
|
|||
|
|
|||
|
Python代码
|
|||
|
\>>> sorted(students, key=itemgetter(1,2)) # sort by grade then by age
|
|||
|
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
|
|||
|
|
|||
|
对字典排序 ,返回由tuple组成的List,不再是字典。
|
|||
|
|
|||
|
Python代码
|
|||
|
\>>> d = {'data1':3, 'data2':1, 'data3':2, 'data4':4}
|
|||
|
\>>> sorted(d.iteritems(), key=itemgetter(1), reverse=True)
|
|||
|
[('data4', 4), ('data1', 3), ('data3', 2), ('data2', 1)]
|
|||
|
\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|||
|
|
|||
|
Sorting basic:
|
|||
|
\>>> print sorted([5, 2, 3, 1, 4])
|
|||
|
[1, 2, 3, 4, 5]
|
|||
|
\>>> L = [5, 2, 3, 1, 4]
|
|||
|
\>>> L.sort()
|
|||
|
\>>> print L
|
|||
|
[1, 2, 3, 4, 5]
|
|||
|
\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|||
|
|
|||
|
Sorting cmp:
|
|||
|
\>>>L = [('b',2),('a',1),('c',3),('d',4)]
|
|||
|
\>>>print sorted(L, cmp=lambda x,y:cmp(x[1],y[1]))
|
|||
|
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
|
|||
|
|
|||
|
\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|||
|
|
|||
|
Sorting keys:
|
|||
|
\>>>L = [('b',2),('a',1),('c',3),('d',4)]
|
|||
|
\>>>print sorted(L, key=lambda x:x[1]))
|
|||
|
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
|
|||
|
\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|||
|
|
|||
|
Sorting reverse:
|
|||
|
\>>> print sorted([5, 2, 3, 1, 4], reverse=True)
|
|||
|
[5, 4, 3, 2, 1]
|
|||
|
\>>> print sorted([5, 2, 3, 1, 4], reverse=False)
|
|||
|
[1, 2, 3, 4, 5]
|
|||
|
|
|||
|
注:效率key>cmp(key比cmp快)
|
|||
|
|
|||
|
在Sorting Keys中:我们看到,此时排序过的L是仅仅按照第二个关键字来排的,如果我们想用第二个关键
|
|||
|
字排过序后再用第一个关键字进行排序呢?
|
|||
|
\>>> L = [('d',2),('a',4),('b',3),('c',2)]
|
|||
|
\>>> print sorted(L, key=lambda x:(x[1],x[0]))
|
|||
|
\>>>[('c', 2), ('d', 2), ('b', 3), ('a', 4)]
|
|||
|
|
|||
|
### 扩展:内置函数列表
|
|||
|
|
|||
|
[]()
|
|||
|
|
|||
|
1. abs()
|
|||
|
|
|||
|
获取一个数字的绝对值
|
|||
|
|
|||
|
2. all()
|
|||
|
|
|||
|
接受一个序列,如果序列内的所有元素都为真,那么返回True,否则返回False
|
|||
|
|
|||
|
3. any()
|
|||
|
|
|||
|
接受一个序列,如果序列内的元素任何一个为真,则返回 True。
|
|||
|
|
|||
|
4. bin() 、oct()、 hex()
|
|||
|
|
|||
|
将十进制数分别转换为2/8/16进制。
|
|||
|
|
|||
|
5. bytes()
|
|||
|
|
|||
|
将一个字符串转换成字节类型
|
|||
|
|
|||
|
```python
|
|||
|
In [63]: bytes('千锋', encoding='utf=8')
|
|||
|
Out[63]: b'\xe5\x8d\x83\xe5\xb3\xb0'
|
|||
|
```
|
|||
|
|
|||
|
6. str()
|
|||
|
|
|||
|
将字符类型等转换为字符串类型, 就是调用一个对象的 __str__ 方法
|
|||
|
|
|||
|
```python
|
|||
|
In [64]: str(b'\xe5\x8d\x83\xe5\xb3\xb0',encoding='utf-8')
|
|||
|
Out[64]: '千锋'
|
|||
|
```
|
|||
|
|
|||
|
7. callable()
|
|||
|
|
|||
|
判断对象是否可以被调用,能被调用的对象就是一个callables对象
|
|||
|
|
|||
|
8. dir()
|
|||
|
|
|||
|
不带参数时返回当前范围内的变量,方法和定义的类型列表,带参数时返回参数的方法列表
|
|||
|
|
|||
|
9. divmod()
|
|||
|
|
|||
|
分别取商和余数
|
|||
|
|
|||
|
```python
|
|||
|
In [66]: divmod(10,3)
|
|||
|
Out[66]: (3, 1)
|
|||
|
|
|||
|
In [67]: divmod(10,2)
|
|||
|
Out[67]: (5, 0)
|
|||
|
```
|
|||
|
|
|||
|
10. enumerate()
|
|||
|
|
|||
|
返回一个可以枚举的对象,该对象的next()方法将返回一个元组。
|
|||
|
|
|||
|
11. eval()
|
|||
|
把字符串转换成python代码去执行(有返回值),不支持复杂表达式
|
|||
|
|
|||
|
```python
|
|||
|
In [44]: eval("3+5")
|
|||
|
Out[44]: 8
|
|||
|
|
|||
|
In [45]: res = eval("3+5")
|
|||
|
|
|||
|
In [46]: res
|
|||
|
Out[46]: 8
|
|||
|
```
|
|||
|
|
|||
|
12. frozenset()
|
|||
|
|
|||
|
创建一个不可修改的集合。
|
|||
|
|
|||
|
```python
|
|||
|
In [71]: s = frozenset([1,2,3])
|
|||
|
|
|||
|
In [72]: s1 = set([1,2,3])
|
|||
|
```
|
|||
|
|
|||
|
13. round()
|
|||
|
|
|||
|
精确度,保留几位小数
|
|||
|
|
|||
|
```python
|
|||
|
# round(x [, n])
|
|||
|
# 对参数x的第n+1位小数进行四舍五入,返回一个小数位数为n的浮点数。
|
|||
|
# 参数n的默认值是0
|
|||
|
|
|||
|
In [74]: round(3.1415926,3)
|
|||
|
Out[74]: 3.142
|
|||
|
```
|
|||
|
|
|||
|
14. print()
|
|||
|
|
|||
|
打印函数,打印内容到标准输出
|
|||
|
print(self, *args, sep=' ', end='\n')
|
|||
|
|
|||
|
```
|
|||
|
import time
|
|||
|
|
|||
|
for i in range(0, 101, 2):
|
|||
|
time.sleep(0.1)
|
|||
|
char_num = i//2
|
|||
|
# print("{}%".format(char_num)) # 打印多少个#
|
|||
|
per_str = '\r{}%: {}\n'.format(i, '#' * char_num) if i == 100 else '\r{}%: {}'.format(i, '#' * char_num)
|
|||
|
print(per_str, end='')
|
|||
|
print('end')
|
|||
|
# \r 换行符,光标在行首
|
|||
|
# \n 回车符,光标在行尾
|
|||
|
```
|
|||
|
|