py是一种类似介于面向对象和面向过程之间的一种语言,即有对象这样的高级概念,也会类似脚本语言的一些特性。本节主要聊下py的函数定义等知识点。

一、基础函数

1.1、函数的定义

虽然可以按脚本语言那样来定义函数,但在函数定义上还是比较建议不要采用隐藏参数的方式,参考代码如下:

_no_value = object()
def default_func(a, b=0, c=None, d=_no_value):
print(f"a={a} and b={b} and c={c} and d ={d}")
if c is None:
print("No b value supplied")
return "RETUREN:" + str(a)

# a=1 and b=0 and c=None and d =<object object at 0x7ffa103a6720>
# No b value supplied
default_func(1)

# a=1 and b=2 and c=3 and d =<object object at 0x7ffa103a6720>
default_func(1, 2, 3)


1.2、函数的解析

有很多这样的元信息,下央只示例一种,代码如下:

def add(x:int, y:int) -> int:
"""注释"""
return x + y

print(f'help info:\n {help(add)}')

输出如下:
Help on function add in module __main__:

add(x: int, y: int) -> int
注释

help info:
None
print(f'annotations: {add.__annotations__}')
#annotations: {'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}

1.3、函数的调用

py的函数调用也比较灵活,建议笔者在使用py函数功能时多从可读性方面着手选用合适的写法。

"""最普通的函数定义"""
def greetUser(userName, age):
"""函数说明"""
print (userName + "-" + str(age));
return "RETUREN:" + userName;

greetUser("ld", 5);#位置 ld-5
greetUser(userName="ld", age=5);#关键字 ld-5
greetUser("ld", age=5);#ld-5

1.4、函数的参数

最简单的函数参数定义,可不定义参数类型也可以定义,带一个星号(​*​)参数的函数传入的参数存储为一个元组​tuple​),带两个(​*​)号则是表示字典(​dict​)。如下:

def default_func(a:int) :

4.1.1、可选参数

以**开头的参数只能出现在最后一个参数中。*:类似于数组,**更倾向于map方式(在py中叫dict)。详细见下面代码:

"""定义任意数量0~n的参数,在函数内部会封装成元组信息"""
def yuanzhuFun(*param):
for item in param:
print(item);
yuanzhuFun("ld","hm"); #ld hm

"""字典参数"""
def mapFun(name, **param):
for k, v in param.items():
print( k +":"+ v, end=',');
mapFun("ld",color="green", age="5"); # color:green,age:5,

4.1.2、强制关键字调用

将强制参数放到某个以*开头或单个*号后面,这样调用者就必须用关键字来传递了,增加了代码的可读性;比**更直观,最好是放在单个*后面,也可以当必传参数来使用。

def recv(*, maxsize,  block, test): #这种写法的话*,只是一个标识
return ''

# recv(1024, True) # TypeError
recv(maxsize=1023, block=True, test=2)
def recv(*param, maxsize,  block, test): #这种写法的话*,param是一个可选参数
recv("ld",maxsize=1023, block=True, test=2)

#这种写法的话,在调用时后面必须要用关键字
def logged(func=None, *, level=logging.DEBUG, name=None, message=None)

4.1.3、参数默认值

"""带默认值的函数定义,默认值的必须在后面"""
def greetUserBydefault(userName, age=5):
print( userName + "-" + str(age));
return "RETUREN:" + userName;
greetUserBydefault("ld"); #位置 ld-5

参数动态默认值,其实就是用partial()函数来固化某些参数的值,防止参数过多时传错了。这个参数有两个作用,来解决代码兼容问题;

def test_func(a, b, c, d):
return a, b, c, d

from functools import partial
tf_val_1 = partial(test_func, 1)
print(f'partial 1: {tf_val_1(2, 3, 4)}') #(1, 2, 3, 4)

tf_val_2 = partial(test_func, d=42)
print(f'partial 42: {tf_val_2(1, 2, 3)}') #(1, 2, 3, 42)

tf_val_3 = partial(test_func, 1, 2, d=42)
print(f'not partial 4: {tf_val_3(4)}') #(1, 2, 4, 42)

1.5、函数值返回值

1.5.1、函数返回类型定义

def is_key(data: dict, key: str) -> bool: 
#指定函数返回值为bool,其它可选的有list, dict, int, str, set, tuple等

1.5.2、不显示指定返回值

def more_return_func():
return 1, 2, 3

a, b, c = more_return_func()
print(f'value of a = {a}') #value of a = 1
print(f'value of b = {b}') #value of b = 2
print(f'value of c = {c}') #value of c = 3

print(f'more return: {more_return_func()}') #(1, 2, 3)

1.5.3、typing模块函数返回类型定义

这个模块可选的类型有Any、Union、Tuple、Callable、TypeVar、Generic等,类型检查,防止运行时出现参数和返回值类型不符合,该模块加入后并不会影响程序的运行,不会报正式的错误。typing也可以用在参数上面。

#元组
def is_key(data: dict, key: Tuple[int]) -> None:
print("")

#全类型覆盖
def is_key(data: dict, key: Any) -> Any:
print("")

二、匿名函数

2.1、简单定义

add = lambda x, y: x + y
print(f'number add result = {add(2, 3)}') #5
print(f"str add result: {add('hello', 'world')}") # hello world

name_list = ['python', 'java', 'go', 'c++']
['c++', 'go', 'java', 'python']
print(f'sorted result: {sorted(name_list, key=lambda name: name.split()[-1].lower())}')

2.2、变量作用域

当内部函数对外部函数作用域的变量进行引用时,就会出现作用域的问题

x = 10
a = lambda y: x + y
x = 20
b = lambda y: x + y

#这处引用的x=20会被二次赋值覆盖掉
print(f'a(20) = {a(20)}')#40
print(f'b(20) = {b(20)}')#40

#这里就各用各的值了
x = 15
print(f'when x=15,a(15) = {a(15)}')#30
x = 3
print(f'when x=3,a(15) = {a(15)}')#18

#注意看x=x,这种定义时绑定的方式
x = 10
a = lambda y, x=x: x + y
x = 20
b = lambda y, x=x: x + y
print(f'a(15) = {a(15)}')#25
print(f'b(15) = {b(15)}')#35

2.3、闭包

定义内部函数,如果想改变值需在sette函数中用nonlocal关键字

def test_func():
n = 0
def func():
print(f'var n = {n}')
def get_n():
return n

def set_n(value):
nonlocal n #通过编写函数来修改内部变量的值
n = value

# Attach as function attributes
func.get_n = get_n
func.set_n = set_n
return func

f = test_func()
f.set_n(10)
print(f'get n is: {f.get_n()}') #10, nonlocal这行不加的话输出0

三、回调函数

3.1、有额外状态信息的回调函数

这种一般用于事件处理器,以及延时任务等,这里也有一个yield的使用(暂时可以不用理,简单点来讲就是一个性能提升的机制)。

def apply_async(func, args, *, callback):
# Compute the result
result = func(*args)

# Invoke the callback with the result
callback(result)


def print_result(result):
print(f'print result. Got: {result}')

def add(x, y):
return x + y

apply_async(add, (3, 5), callback=print_result)

apply_async(add, ('Hello ', 'World'), callback=print_result)
#print result. Got: 8
#print result. Got: Hello World
class ResultHandler:
def __init__(self):
self.sequence = 0

def handler(self, result):
self.sequence += 1
print(f'result handler. [{self.sequence}] Got: {result}')


r = ResultHandler()
apply_async(add, (3, 5), callback=r.handler)
apply_async(add, ('Hello ', 'World'), callback=r.handler)
#result handler. [1] Got: 8
#result handler. [2] Got: Hello World
def make_handler():
sequence = 0
def handler(result):
nonlocal sequence
sequence += 1
print(f'make handler. [{sequence}] Got: {result}')
return handler

handler = make_handler()
apply_async(add, (3, 5), callback=handler)
apply_async(add, ('Hello ', 'World'), callback=handler)
#make handler. [1] Got: 8
#make handler. [2] Got: Hello World
def make_handler():
sequence = 0
while True:
result = yield
sequence += 1
print(f'make handler use generator. [{sequence}] Got: {result}')


handler = make_handler()
next(handler)
apply_async(add, (3, 5), callback=handler.send)
apply_async(add, ('Hello ', 'World'), callback=handler.send)

3.2、内联回调函数

def apply_async(func, args, *, callback):
# Compute the result
result = func(*args)

# Invoke the callback with the result
callback(result)


from queue import Queue
from functools import wraps

class Async:
def __init__(self, func, args):
self.func = func
self.args = args

def inlined_async(func):
@wraps(func)
def wrapper(*args):
f = func(*args)
result_queue = Queue()
result_queue.put(None)
while True:
result = result_queue.get()
try:
a = f.send(result)
apply_async(a.func, a.args, callback=result_queue.put)
except StopIteration:
break
return wrapper


def add(x, y):
return x + y

@inlined_async
def test():
result = yield Async(add, (3, 5))
print(f'number add result: {result}')
result = yield Async(add, ('Hello ', 'World'))
print(f'str add result: {result}')
for n in range(10):
result = yield Async(add, (n, n))
print(f'async cycle result: {result}')
print('Goodbye')


if __name__ == '__main__':
import multiprocessing
pool = multiprocessing.Pool()
apply_async = pool.apply_async

test()

number add result: 8
str add result: Hello World
async cycle result: 0
async cycle result: 2
async cycle result: 4
async cycle result: 6
async cycle result: 8
async cycle result: 10
async cycle result: 12
async cycle result: 14
async cycle result: 16
async cycle result: 18
Goodbye

附、将类转换为函数(简化代码)

from urllib.request import urlopen
class UrlTemplate:
def __init__(self, template):
self.template = template

def open(self, **kwargs):
return urlopen(self.template.format_map(kwargs))

bai_du = UrlTemplate('http://baidu.com/s?swd={name_list}&rsv_spt={field_list}')
for line in bai_du.open(name_list='python,java,go', field_list='1'):
print(line.decode('utf-8'))
"""与上面代码等价"""
def url_template(template):
def opener(**kwargs):
return urlopen(template.format_map(kwargs))
return opener #注意这行是返回一个函数

bai_du = url_template('http://baidu.com/s?swd={name_list}&rsv_spt={field_list}')
"""这个循环相当于调用了urlopen()三次"""
for line in bai_du(name_list='python,java,go', field_list='1'):
print(line.decode('utf-8'))