今天讲一些 python 中的高级用法, 有助于大家更好的使用 python 这门语言今天讲的这些知识是层层递进的关系, 前面是后面的铺垫
函数可变参数 * args 和 **kwargs
python 支持固定参数, 默认参数, 也和很多其他语言一样支持可变参数, 只不过 python 支持的可变参数分为两种,*args 是 tuple, 里面可以有任意多个 element(包括 0 个)**kwargs 则是当你需要指定 key word 时需要用到的参数类型
先考虑 * args 的情况, 先看函数定义:>>> def take_any_args(*args):
- ... print("type of args:" + str(type(args)))
- ... print("value of args:" + str(args))
- ...>>> take_any_args(1)
- type of args:<type 'tuple'>
- value of args:(1,)>>> take_any_args("a","b","c")
- type of args:<type 'tuple'>
- value of args:('a', 'b', 'c')>>> take_any_args()
- type of args:<type 'tuple'>
- value of args:()>>> take_any_args(['1','2'],['3','4'])
- type of args:<type 'tuple'>
- value of args:(['1', '2'], ['3', '4'])
再看参数提取:
- def print_args(*args):
- for arg in args:
- print(arg)
- print_args("red", "blue", "green")
- def print_all(*args, **kwargs):
- for arg in args:
- print(arg)
- for key, value in kwargs.items():
- print("{} -> {}".format(key, value))
- print_all(1, 2)
- print_all(a="red", b="blue", c="green")
- red
- blue
- green
- 1
- 2
- a -> red
- c -> green
- b -> blue
下面看看为什么需要 **kwargs, 对于上面的 print_args, 下面这种添加了 keyword 的调用方式会出错, 所以就有了 **kwargs 的用武之地:>>> def print_args(*args):
- ... for arg in args:
- ... print arg
- ...>>> print_args(a=1,b=2)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: print_args() got an unexpected keyword argument 'a'
**kwargs 的本质其实是 dict, 如下所示:>>> def print_kwargs(**kwargs):
- ... for key,value in kwargs.items():
- ... print("{}->{}".format(key,value))
- ...>>> print_kwargs(a="lalala",b="papapa")
- a->lalala
- b->papapa
通常再使用的时候都是二者合起来使用, 如下所示:
- def print_all(*args, **kwargs):
- for arg in args:
- print(arg)
- for key, value in kwargs.items():
- print("{} -> {}".format(key, value))
- print_all(1, 2)
- print_all(a="red", b="blue", c="green")
- 1
- 2
- a -> red
- c -> green
- b -> blue
上面的知识大家差不多应该都知道, 下面这种 Unpacking 的用法很多人都不太了解:>>> def sample_function(a,b,c):
- ... print("{},{},{}").format(a,b,c)
- ...>>> input = (1,2,3)>>> sample_function(1,2,3)
- 1,2,3
- # 和上面的方法等效 >>> sample_function(*input)
- 1,2,3
unpack 使用 kwargs, 记住 keyword 要和函数声明时的变量名一致才行, 否则会报错 >>> def sample_function(a,b,c):
- ... print("a->{},b->{},c->{}".format(a,b,c))
- ...>>> input = {"a":1,"b":2,"c":3}>>> sample_function(**input)
- a->1,b->2,c->3
- # 与上面方法等效 >>> sample_function(a=input['a'],b=input['b'],c=input['c'])
- a->1,b->2,c->3
- lambda function
在 python 中所有的东西都是 object, 不管是 int 也好, list 也好都是 object 函数也是 object 这个概念很重要 >>> def f(n):
- ... return n+1
- ...>>> id(f)
- 4374076184>>> g = f>>> print g(2)
- 3>>> id(g)
- 4374076184
上面的 g 和 f 所指向的 object 是同一个 object
下面思考这样一个问题, 如果 numbers = ["10", "3", "40", "14", "5"], 让你找出最大值怎么找?>>> max(numbers)
'5'
这显然不对, 因为 max 默认按照字母顺序排序了, 所以需要额外提供排序信息:>>> max(numbers, key=int)
'40'
int 就是一个 function, 然后看看如果用 lambda function 表示就是:>>> max(numbers, key=lambda x:int(x))
'40'
再举一个例子, 下面是几个人的年龄, 性别, 地址, 请找出年纪最大的人:>>> person_zhangsan = {'age': 40, 'gender': 'male', 'home': 'beijing'}>>> person_lisi = {'age': 35, 'gender': 'male', 'home': 'hangzhou'}>>> person_wangwu = { 'age': 21, 'gender': 'female', 'home': 'chongqing'}>>> people = [person_zhangsan, person_lisi, person_wangwu]>>> max(people, key=lambda x:x['age'])
{'gender': 'male', 'age': 40, 'home': 'beijing'}
python 在 operator 中提供了 itemgetter 这个函数, 它起到的作用和 lambda function 一样, 比如:>>> from operator import itemgetter>>> max(people, key=itemgetter("age"))
{'gender': 'male', 'age': 40, 'home': 'beijing'}
对比一下我还是更喜欢 lambda function 的定义, 简洁明了
Decorator 装饰器
最长见的 decorator 的 user case 是什么? 答: retry 比如网络 restful request 碰到不稳定的 server 或者说 server 给你返回了 5XX, 你要不要 retry
一开始可能你的 code 长这样:
- import requests
- URL = "https://example.com/api"
- def get_items():
- return requests.get(URL + "/items")
当然你还会有很多 get function, 比如 get_apple, get_banana, get_orange, ...
实际部署之后发现 server 不稳定, 不定期返回 500, 你就要加 retry
如果只有一个 get_items, 你可能会这么写:
- # 第二版, 加入 retry
- def get_items():
- NUM_RETRY = 3
- current_retry = 0
- resp = None
- while True:
- resp = requests.get(URL + "/items")
- if rest.status_code/100 == 5 and current_retry <NUM_RETRY:
- current_retry += 1
- continue
- break
- return resp
可是每一个 fucntion 都要改, 是不是很累
下面 decorator 隆重登场, decorator 的本质是一个 function 这个 function 的 parameter 有且仅有一个就是一个 function object, 返回值则是另一个不同的 function
- # 比如已经有了一个普通 function
- def some_function(arg1,arg2,arg3):
- #此处省略 20 行
- some_function = some_decorator(some_function)
等效于
- @some_decorator
- def some_function(arg1,arg2,arg3):
- #......
下面举一个 decorator 的例子, logging decorator
- def logfuncname(func):
- def wrapper(*args, **kwargs):
- print("function name:" + func.__name__)
- return func(*args, **kwargs)
- return wrapper>>> @logfuncname
- ... def some_func(n):
- ... return n+1
- ...>>> print some_func(3)
- function name: some_func
- 4
如上所示, logfuncname 就是一个 decorator, 它的 input 是 func,return 了一个 wrapper function
下面我们回到一开始 retry 那个例子:
- # 第三版, 定义 decorator
- def retry(func):
- def wrapper(*args, **kwargs):
- NUM_RETRY = 3
- current_retry = 0
- resp = None
- while True:
- resp = func(*args, **kwargs)
- if rest.status_code/100 == 5 and current_retry <NUM_RETRY:
- current_retry += 1
- continue
- break
- return resp
- return wrapper
- @retry
- def get_items():
- return requests.get(URL + "/items")
然后 get_apple, get_banana, get_orange 什么的上面加上 @retry 就可以了
接下来问题来了, 如果有另一个 decorator 也想用上怎么办?
decorator 是可以叠加的, 比如下面的例子, 注意上下顺序就是 decorator 从左到右的顺序
- @add2
- @multi3
- def foo(n):
- return n + 1
- # 相当于 foo = add2(multi3(foo))
- # 那么 foo(3) 就是 14
- @multi3
- @add2
- def foo(n):
- return n + 1
- # 相当于 foo = multi3(add2(foo))
- # 那么 foo(3) 就是 18
如果想要改变 retry 的次数怎么办, 比如 get_apple 想要 retry 3 次, 但是 get_banana 想要 retry 5 次怎么办?
- # 第四版, 定义带参数的 decorator
- def retry(num_retry):
- def decorator(func):
- def wrapper(*args, **kwargs):
- current_retry = 0
- resp = None
- while True:
- resp = func(*args, **kwargs)
- if rest.status_code/100 == 5 and current_retry < num_retry:
- current_retry += 1
- continue
- break
- return resp
- return wrapper
- return decorator
- @retry(3)
- def get_items():
- return requests.get(URL + "/items")
这里其实用到了一个 closure 的概念, 就是外层函数的参数在里层函数里是可见的, 而里层函数的参数在外层不可见 (当然这里也不需要)
Decorator 在 flask 中的实现原理
下面我们来看看 flask 中 decorator 是怎么实现的, 简而言之:
- class webApp:
- def __init__(self):
- #初始化 routes
- self.routes = {}
- def route(self, param):
- def decorator(func):
- #定义 decorator 时为 routes 赋值 key/value
- self.routes[param] = func
- def wrapper(*args, **kwargs):
- return func(*args, **kwargs)
- return wrapper
- return decorator
- def get(self, param):
- try:
- #get 时根据 key 返回 value
- return self.routes[param]()
- except KeyError:
- return "ERROR - no such page">>> app = WebApp()>>> @app.route("/")
- ... def index():
- ... return 'Index Page'
- ...>>> @app.route("/contact/")
- ... def contact():
- ... return 'Contact Page'
- ...>>> app.get("/")
- 'Index Page'>>> app.get("/contact/")
- 'Contact Page'>>> app.get("/no-such-page/")
- 'ERROR - no such page'
来源: https://www.cnblogs.com/huashao1985/p/8548470.html