1. 函数
def fun():
"""
description
"""
help(fun) # 可以显示函数的描述性文档
1.1 函数与方法
函数不是方法
方法对应的是类和对象
1.2 函数赋值
https://www.runoob.com/w3cnote/python-func-decorators.html
2. 参数
函数传递可变参数和不可变参数
http://c.biancheng.net/view/4471.html
2.1 位置实参
按照形参的顺序传递参数
位置参数在传参的时候也可以利用赋值的方法进行传递
def demo(a,b=10):
pass
# 说明关键字赋值法兼容位置参数法
data=30
# version 1
demo(data,b=30)
# version 2
demo(b=30,a=20)
# version 3
demo(b=30,a=data)
2.2 关键字实参
是传递给函数的名称-值对
直接在传实参的时候,将形参显示的指定
关键字参数在声明形参,和实参时,都必须在位置参数的后面
关键字参数不关注顺序
关键字参数兼容位置参数
# 例子1
data={'c':300,'a':100,'b':200}
def demo1(a,b,c):
print(a)
print(b)
print(c)
# !!! 这种方式传递的时候,必须关键字key值相同
# 无关顺序
demo1(**data) # 100,200,300
# 例子2
def demo2(pos,*args,**kwargs):
print('required',pos)
print('optional args',args)
print('optional kwargs',kwargs)
demo2(**data) # wrong! 少一个pos参数
data2={'pos':'pos','c':300,'a':100,'b':200}
demo2(**data2) # 结果如下
# required posc # 有pos位置参数,因为关键字参数兼容位置参数
# optional args () # 没有不定长位置参数,所以不会被绑定到不定长参数上
# optional kwargs {'c': 300, 'a': 100, 'b': 200} # 剩下的都是不定长关键字参数
# 尽管是不定长的参数,但是优先必选参数
# 例子3
def demo3(pos,**kwargs):
print('pos': pos)
print('kwargs':kwargs)
demo3({'pos':100,'a':200,'b':300})
# pos 100
# {'a':200,'b':300}
2.3 可变参数 args/kwargs
*args,**kwargs
https://blog.csdn.net/github_37761578/article/details/61416228
https://www.zhihu.com/search?q=python%20args%20kwargs&utm_content=search_suggestion&type=content
见上面的例子
位置参数与关键字参数的混合
def demo(*args, **kwargs):
print(args)
print(kwargs)
gl_nums = (1, 2, 3)
gl_dict = {"name": "小明", "age": 18}
demo(gl_nums, gl_dict)
# 输出
# ((1, 2, 3), {‘name’: ‘小明’, ‘age’: 18})
# {}
demo(*gl_nums, **gl_dict)
# 输出
# (1, 2, 3)
# {‘name’: ‘小明’, ‘age’: 18}
2.4 任意位置参数
def fcn_name(*value):
pass
fcn_name(value1)
fcn_name(value1,value2)
value表示创建一个空元组,然后将由实参传递到的所有只都封装到这个元组中
2.5 任意关键字参数
def fcn_name(value1,**user_info):
for key,value in user_info:
pass
fcn_name(value1,location='princeton',field='physics')
https://www.runoob.com/python3/python3-function.html
user_info表示创建一个空字典,把接收到的名-值对都封装到字典中
2.6 __ defaults __ 默认参数
查看默认参数
fun_name.defaults 方法
def func(a:int=1,b:str='')->bool:
pass
# (1, '')
2.7 __ annotation __ 注释
def func(a:int=1,b:str='')->bool:
pass
# {'a': <class 'int'>, 'b': <class 'str'>, 'return': <class 'bool'>}
2.8 默认参数注意事项
避免使用可变对象作为默认参数
class C(object):
def __init__(self,x=[]): # 应该避免这种写法的出现!!!
self.x=x
# 可以改成
class C(object):
def __init__(self,x=None):
if x:
self.x=x
else:
self.x=[]
2.9 posonly/keywordsonly参数
position only params
/
前,只接受位置参数,不接受关键字参数的传递(即使是关键字参数兼容位置参数也不行)
def f(a,b=3,/,*args,**kwargs):
pass
f(1) # 可以
f(1,1) # 可以
f(a=1) # 不可以
f(1,b=10) # 不可以,b不会被赋值为10,关键字传参失效
f(1,0,**data) # 可以
keywords only params
*
后面,值接受关键字参数,不接受位置参数
def f(a,*args,*,b=3,,**kwargs):
pass
data={'1':1,'2':2,'b':20}
f(1,**data) # 可以
3. 传参与返回
返回可以是字典
传参可以是列表,列表中可以是任何的对象(包括字典)
传递列表后,对列表修改则是永久性的
对于传递列表可以利用切片法传递副本
4. 命名空间/变量作用域
http://c.biancheng.net/view/2259.html
4.1 命名空间
返回的是字典形式
- 内置命名空间:内置的命名,如内置函数等
- 全局命名空间:模块中定义的名称,如函数、类、其他导入的模块、模块级变量和常量
- 局部命名空间:函数中定义的名称,如函数的参数、局部定义的变量,类中定义的函数也算局部命名空间
命名空间查找顺序
局部命名空间 --> 全局命名空间 --> 内置命名空间
4.2 变量作用域
命名空间的生命周期,取决于对象的作用域,如果对象执行完成,则该命名空间的声明周期结束
变量访问机制:从内到外的访问所有的作用域(不能从外往内访问所有的作用域)
注意!!!:变量并不是哪个位置都可以访问到,访问权限决定于这个变量是在哪里赋值的
local
:最内存层,一个函数或方法的内部
non-local
闭包函数中外层函数内的变量
global
当前脚本的最外层,如,当前模块的全局变量
built-in
内建变量与关键字
只有模块module、类class、def/lambda、列表推导式等才会引入新的作用域,其他代码块,如
if
、try/except
、for/while
等不会引入新的作用域,也就是说这些语句内定义的变量,外部也能访问!!!
globals()
全局命名空间内的变量
globals()
在函数内部调用的话,显示的也是全局命名空间内的变量
使用globals().items()
时的错误注意事项 https://blog.csdn.net/qq_31362767/article/details/103267634
-
locals()
当前命名空间内的变量,(全局下调用locals则‘当前’包括‘全局’) -
vars() # 注意vars函数的使用
-
non-local
https://www.runoob.com/python3/python3-namespace-scope.html
def f():
print('f')
def ff():
f()
print('ff')
class C:
def fff(self):
ff()
c=C()
c.fff()
# f
# ff
# 例2
def f():
print('f')
def ff():
f()
print('ff')
class C:
def fff(self):
self.func=ff
c=C()
c.fff()
c.func()
# f
# ff
可变对象和不可变对象
即使是在闭包函数内,对non-local变量对象的修改也是需要注意的
对于不可变对象,只是实现了_add__
等类似的方法,会赋值给新的不可变对象,不是inplace操作,这相当于重新赋值了,所以python会将其当做局部变量来对象他,所以可能出现错误
因此non-local关键字诞生了
a=1
b=[]
def f():
# a+=1 # wrong!
b.append(10)
print(a) # 1
print(b) # 注意,这里会修改可变对象
注意!!!
太可怕了,之前没有注意到
在下述的例子中,python编译函数的定义体的时候,将b判断为局部变量,因为在函数中给b赋值了,因为Python设定 在函数定义体中赋值的变量为 局部变量
a=1
b=2
def f(a):
print(a)
print(b)
b=20
# 在打印print(b) 的时候报错了 UnboundLocalError: local variable 'b' referenced before assignment
__ code__ /codeobjet
进阶,见python-进阶https://blog.csdn.net/L_fengzifei/article/details/123559473
object.__code__.co_varnames # 查看对象中保存的局部变量,object可以是类,也可以使函数
object.__code__.co_freevars # 查看对象中保存的自由变量
5. 高级注解
变量注解与函数参数注解
https://blog.csdn.net/ybdesire/article/details/102633291
a:str=[] # 并不是强制类型转换
6. 函数
#创建函数
def function_name(parameters):
"""描述函数功能的可选文档字符串"""#三引号便于文档字符串扩展到多行,print(greet.__doc__)可以打印这个文档文件
statements(s)#函数体
return # 如果没有返回值就返回None
函数参数可以有默认值,但是一旦一个参数有默认值,那么右边的所有参数都有默认值
python允许使用关键字调用参数,即可以改变顺序
>>> # 2 keyword arguments
>>> greet(name = "Bruce",msg = "How do you do?")
>>> # 2 keyword arguments (out of order)
>>> greet(msg = "How do you do?",name = "Bruce")
>>> # 1 positional, 1 keyword argument
>>> greet("Bruce",msg = "How do you do?")
有时,我们事先并不知道将传递给函数的参数数量.Python允许我们通过具有任意数量参数的函数调用来处理这种情况
在函数定义中,我们在参数名称前使用星号(*)来表示这种参数。这是一个例子
def greet(*names):
"""This function greets all
the person in the names tuple."""
# names is a tuple with arguments
for name in names:
print("Hello",name)
greet("Monica","Luke","Steve","John")
"""
在这里,我们使用多个参数调用该函数。这些参数在传递给函数之前被包装到元组中。在函数内部,我们使用for循环来检索所有参数"""
#剔除元素
def remove_duplicates(duplicate):
uniques = []
for num in duplicate:
if num not in uniques:#有not in的用法
uniques.append(num)
return(uniques)#return可以返回多个值
duplicate = [2, 4, 10, 20, 5, 2, 20, 4]
print(remove_duplicates(duplicate))
6.1 偏函数
函数的二次封装作用,用于减少参数传递
http://c.biancheng.net/view/5674.html
- 偏函数的二次封装是一种静态的形式,只是减少参数量
- 而函数中是可以见函数作为参数传递的,这是一种二次封装调用的形式。http://c.biancheng.net/view/2261.html
6.2 局部函数/闭包函数-还要再看
局部函数与nolocal
http://c.biancheng.net/view/2260.html
https://www.runoob.com/python3/python3-namespace-scope.html
https://www.runoob.com/w3cnote/python-func-decorators.html
带参局部函数变成了闭包函数
闭包函数的调用机制与返回值http://c.biancheng.net/view/5335.html
https://www.runoob.com/w3cnote/python-func-decorators.html
6.2.1 闭包函数
使用了最外层函数的局部函数,即使用了non-local变量
(当某个函数被当做对象返回,夹带了外部变量,就形成了闭包)
闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些“私有”变量能够保持起持久性
实际上这些non-local变量术语叫做 自由变量,延伸到闭包函数的内部函数中
外部函数的形参(形参也是局部变量)和局部变量不都会被释放,缺点:未释放的变量占用内存
def preson(name):
num=100
def say(content):
print(f'{name}:{num},{content}')
return say
preson1=preson('name')
preson1('content')
修改non-local变量
def counter(start=0):
def add():
nonlocal start
start+=1
return start
return add
# 独立创建两个闭包函数,没有任何关联
c1=counter(1)
c2=counter(20)
print(c1()) # 2
print(c2()) # 21
print(c1()) # 3
print(c2()) # 22
闭包函数与类对象的关系
类对象,可以理解为数据(属性)+功能(方法)
闭包函数,也可以理解为数据+功能,闭包的数据可以看成是外部函数的局部变量,功能看做是内部函数
类对象适合完成复杂的功能,闭包适合轻量功能
闭包进阶
def test(func):
pass
def a():
pass
b=lambda : None # :之前是参数,:之后是表达式
def close(name):
def c(content):
pass
return c
class CFunc:
def __init__():
pass
test(a) # func是函数a的引用,只传递功能,不传递数据
test(b) # func是匿名函数b的引用,只传递功能,不传递数据
test(close('name')) # func是闭包函数中内部函数c的引用,即传递功能,也传递数据(name)
test(CFunc(xxx)) # func是实例类对象的引用
6.3 eval() 和 exec()-没看完
执行字符串形式的表达式(也可以是字符串形式的代码段)
http://c.biancheng.net/view/5683.html
区别:eval
有返回值,exec
没有返回值
命名空间与globals???在eval和exec中的应用???
eval()
# 例1 与locals有关
a=10
b=20
c=30
g={'a':6,'b':8}
t={'b':100,'c':10}
print(eval('a+b+c',g,t)) # 116
# 例2
eval("{'a':2}") # 返回一个字典{'a':2}
# 例3 不执行赋值操作,但是可以执行比较操作,因为赋值操作没有返回值
eval('a=2') # error: eval函数中没有赋值操作
exec()
通常的使用场景是:动态赋值
# 例1 与 globals 有关
dic={}
dic['b']=3
print(dic.keys()) # ['b']
exec('a=3',dic)
print(dic.keys())
# 例2
b=20
exec('b=2')
print(b) # 2
exec("v={'m':a,'n':b}")
print(v) # {'m': 2, 'n': 2}
6.4 列表推导式与map() / filter() / reduce()
https://blog.csdn.net/answer3lin/article/details/86505932
map()
用于映射
map()
返回迭代器,需要用list
进行转化
注意map()
返回的迭代器,没有建立stopIteration
异常处理机制,所以,for
了一次之后,无法``for```第二次
a = [1, 2, 3, 4]
b = map(lambda x: x*2, a)
# 第一次
for x in b:
print(x) # 2,4,6,8
# 第二次
for x in b:
print(x) # 没有返回值直接是None
map可以处理多个序列长度输入
可以支持多个参数的函数,注意的是各序列的长度必须一样,否则报错
map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
map可以应用多个函数
https://blog.csdn.net/answer3lin/article/details/86505932
def multiply(x):
return (x*x)
def add(x):
return (x+x)
funcs = [multiply, add]
for i in range(5):
value = map(lambda x: x(i), funcs)
print(list(value))
# 译者注:上面print时,加了list转换,是为了python2/3的兼容性
# 在python2中map直接返回列表,但在python3中返回迭代器
# 因此为了兼容python3, 需要list转换一下
# Output:
# [0, 0]
# [1, 2]
# [4, 4]
# [9, 6]
# [16, 8]
filter()
filter
用于过滤序列
filter
同样返回的是迭代器对象
filter(function or None, sequence) -> list, tuple, or string
def is_odd(n):
return n % 2 == 1
tmplist = filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
newlist = list(tmplist)
print(newlist)
[1, 3, 5, 7, 9]
- 列表推导式与map filter
# 列表推导式
s='string'
s1=[ord(x) for x in s if ord(x)>127]
# filter/map
s2=list(filter(lambda x:x>127,map(ord,s)))
- reduce
累计处理对象元素
先取可迭代对象中的一对元素,计算一个结果,返回,然后将返回的结果与下一个元素组成一对进行计算,以此类推,累积计算
from functools import reduce
reduce(lambda a,b:a*b,range(10)) # 0
reduce(lambda a,b:a+b,range(10)) # 45
recude使用指南
reduce(function,iterable,initializer)
,避免抛出异常,如果序列为空initializer
作为返回值,当不为空的时候,将其作为第一个参数,对于+、-、|、^
来说,initializer
是0,对于*、&
是1
map与reduce
map 是逐个元素执行算子,元素与元素之间没有关系
reduce 是累积执行算子,元素之间有关系
6.5 any()/all()
all([]) # True
any([]) # False
6.6 zip()
zip(iterable,iterable)
返回的是生成器
zip
可以处理多个可迭代对象
zip
处理不同长度的时候,会在最短的可迭代对象耗尽时停止,但是不会发出警告
zip 进阶
from itertools import zip_longest
# zip_logest 可以对元素少的可迭代对象进行元素填充
zip_longest(iterable1,iterable2,fillvalue=value)
7. 函数也是对象
证明函数是对象的例子,如下
def f():
pass
type(f) # <class 'function'>
print(f) # 等价于f, 默认情况下返回的是该函数的内存地址(引用(指向)该函数的内存地址)
dir(f) # __repr__方法也在其中