python函数

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、列表推导式等才会引入新的作用域,其他代码块,如iftry/exceptfor/while等不会引入新的作用域,也就是说这些语句内定义的变量,外部也能访问!!!

  • globals()全局命名空间内的变量

globals()在函数内部调用的话,显示的也是全局命名空间内的变量
使用globals().items()时的错误注意事项 https://blog.csdn.net/qq_31362767/article/details/103267634

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__方法也在其中
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值