深入理解python中的函数

    xiaoxiao2023-11-14  159

    一等函数

    把函数视作对象

    Python 函数是对象。这里我们创建了一个函数,然后调用它,读取它的__doc__ 属性,并且确定函数对象本身是function 类的实例

    def factorial(n): """return n!""" return 1 if n < 2 else n*factorial(n-1) print(factorial(10)) # 3628800 # __doc__ 是函数对象众多属性中的一个,用于生成对象的帮助文本 print(factorial.__doc__) # return n! print(type(factorial)) # <class 'function'> # 我们可以把factorial 函数对象给变量fact,然后通过变量名调用 fact = factorial print(fact) # <function factorial at 0x0000021A9F7E8400> 120 print(fact(5)) # 120 list(map(fact, range(11))) # [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]

    高阶函数

    接受函数为参数,或者把函数作为结果返回的函数是高阶函数(higher-order function)

    fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana'] # 内置函数sorted就是高阶函数:可选的key 参数用于提供一个函数 sorted(fruits, key=len) # ['fig', 'apple', 'cherry', 'banana', 'raspberry', 'strawberry'] def reversed(word): return word[::-1] sorted(fruits, key=reversed) # ['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry'] # 匿名函数lambda sorted(fruits, key=lambda word:word[::-1]) ['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']

    除此之外,常见的高阶函数还有map,filter, functools.reduce,map 和filter 返回生成器。

    可调用对象 除了用户定义的函数,调用运算符(即())还可以应用到其他对象上。如果想判断对象能否调用,可以使用内置的callable() 函数

    >>> [callable(obj) for obj in (abs, str, 13, reversed)] [True, True, False, True] """ 不仅Python 函数是真正的对象,任何Python 对象都可以表现得像函数""" class BingoCage(object): def __init__(self, items): self.items = list(items) def pick(self): try: return self.items.pop() except IndentationError: raise LookupError('pick from empty Bingocage') def __call__(self): print('call me!') return self.pick() >>> bingo = BingoCage(range(3)) >>> bingo.pick() 2 >>> bingo() call me! 1 >>> bingo() call me! 1

    函数内省 使用dir()函数查看函数的属性

    >>> dir(factorial) ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', ...]

    仅限关键字参数 调用函数时使用* 和**是可迭代对象,映射到单个参数

    def func(name, *args, age=18, **kwargs): for s in args: print(s) if kwargs: for k,v in kwargs.items(): print(k,v) print("age is :",age) print("my name is :",name) >>> func('john') age is : 18 my name is : john """ 第一个参数后面的任意个参数会被*args 捕获,存入一个元组 """ >>> func('john','alice','bob') alice bob age is : 18 my name is : john """ 没有明确指定名称的关键字参数会被**kwargs 捕获,存入一个字典 age参数只能作为关键字参数传入""" >>> func('john','alice',age=30,hallie=12, eric=31) eric 12 age is : 18 my name is : zude

    operator模块 operator 模块为多个算术运算符提供了对应的函数,从而避免编写lambda a, b: a*b 这种匿名函数

    from functools import reduce from operator import mul from operator impoer itemgetter def fact1(n): return reduce(lambda a,b:a*b, range(1, n+1)) def fact2(n): return reduce(mul ,range(1, n+1)) >>> metro_data = [ ... ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)), ... ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889))] # 任何实现__getitem__ 方法的类都可以使用itemgetter >>> for city in sorted(metro_data, key=itemgetter(1)): print(city)

    使用一等函数实现设计模式

    来看一个小例子,假如一个网店制定了下述折扣规则

    有1000 或以上积分的顾客,每个订单享5% 折扣。同一订单中,单个商品的数量达到20 个或以上,享10% 折扣。订单中的不同商品达到10 个或以上,享7% 折扣。 简单起见,我们假定一个订单一次只能享用一个折扣。 我们使用抽象基类和函数实现策略,代码如下 from abc import ABC, abstractmethod from collections import namedtuple Customer = namedtuple('Customer', 'name fidelity') class LineItem(object): def __init__(self, product, quantity, price): self.product = product self.quantity = quantity self.price = price def total(self): return self.price * self.quantity class Order(object): def __init__(self, customer, cart, promotion=None): self.customer = customer self.cart = list(cart) self.promotion = promotion def total(self): if not hasattr(self, '__total'): self.__total = sum(item.total() for item in self.cart) return self.__total def due(self): if self.promotion is None: discount = 0 else: discount = self.promotion.discount(self) return self.total() - discount def __repr__(self): fmt = '<Order total: {:.2f} due: {:.2f}>' return fmt.format(self.total(), self.due()) # 把Promotion定义为抽象基类(Abstract Base CLASS), # 为了使用@abstractmethod装饰器,表明所用的模式 class Promotion(ABC): @abstractmethod def discount(self, order): """返回折扣金额(正值)""" class FidelityPromo(Promotion): """为积分为1000或以上的顾客提供5%折扣""" def discount(self, order): return order.total() * .05 if order.customer.fidelity >= 1000 else 0 class BulkItemPromo(Promotion): """单个商品为20个或以上时提供10%折扣""" def discount(self, order): discount = 0 for item in order.cart: if item.quantity >= 20: discount += item.total() * .1 return discount class LargeOrderPromo(Promotion): """订单中的不同商品达到10个或以上时提供7%折扣""" def discount(self, order): distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: return order.total() * .07 return 0

    结果测试如下:

    joe = Customer('John Doe', 0) ann = Customer('Ann Smith', 1100) cart = [LineItem('banana', 4, .5), LineItem('apple', 10, 1.5), LineItem('watermellon', 5, 5.0)] Order(joe, cart, FidelityPromo()) # <Order total: 42.00 due: 42.00> Order(ann, cart, FidelityPromo()) # <Order total: 42.00 due: 39.90>

    我们也可以进行迭代操作,找到折扣额度最大的

    promos = [fidelity_promo, bulk_item_promo, large_order_promo] def best_promo(order): """选择可用的最佳折扣""" return max(promo(order) for promo in promos)

    函数装饰器和闭包

    装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行。这通常是在导入时(即Python 加载模块时),如示例所示

    registry = [] def register(func): print('running register(%s)' % func) registry.append(func) return func @register def f1(): print('running f1()') @register def f2(): print('running f2()') def f3(): print('running f3()') def main(): print('running main()') print('registry ->', registry) f1() f2() f3() if __name__=='__main__': main() results: running register(<function f1 at 0x00000220D84EA488>) running register(<function f2 at 0x00000220D84EA1E0>) running main() registry -> [<function f1 at 0x00000220D84EA488>, <function f2 at 0x00000220D84EA1E0>] running f1() running f2() running f3()

    函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用 时运行。这突出了Python 程序员所说的导入时和运行时之间的区别

    闭包 在函数体内定义函数

    def make_averager(): series = [] def averager(new_value): series.append(new_value) total = sum(series) return total/len(series) return averager avg = make_averager() print(avg(10)) # 10.0 print(avg(11) # 10.5 """ series 是make_averager 函数的局部变量,因为那个函数的定义体中初始化了 series:series = []。可是,调用avg(10) 时,make_averager 函数已经返回了, 而它的本地作用域也一去不复返了 """ # 审查返回的average对象 avg.__code__.co_varnames # ('new_value', 'total') # 自由变量 avg.__code__.co_freevars # ('series',) # 有个cell_contents 属性,保存着真正的值 avg.__closure__[0].cell_contents # [10, 11]

    nonlocal声明

    def make_averager(): count = 0 total = 0 def averager(new_value): count += 1 total += new_value return total / count return averager avg = make_averager() print(avg(10)) --------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) <ipython-input-25-3f4d448a9326> in <module>() 9 10 avg = make_averager() ---> 11 print(avg(10)) <ipython-input-25-3f4d448a9326> in averager(new_value) 3 total = 0 4 def averager(new_value): ----> 5 count += 1 6 total += new_value 7 return total / count UnboundLocalError: local variable 'count' referenced before assignment

    问题是,当count 是数字或任何不可变类型时,count += 1 语句的作用其实与count = count + 1 一样。因此,我们在averager 的定义体中为count 赋值了,这会把count 变成局部变量。total 变量也受这个问题影响。上个例子没遇到这个问题,因为我们没有给series 赋值,我们只是调用series.append,并把它传给sum 和len。也就是说,我们利用了列表是可变的对象这一事实。 但是对数字、字符串、元组等不可变类型来说,只能读取,不能更新。如果尝试重新绑定,例如count = count+1,其实会隐式创建局部变量count。这样,count 就不是自由变量了,因此不会保存在闭包中

    使用nonlocal把变量标记为自由变量,即使在函数中为变量赋予新值了,也会变成自由变量

    def make_averager(): count = 0 total = 0 def averager(new_value): nonlocal count, total count += 1 total += new_value return total/count return averager avg = make_averager() print(avg(10)) # 10.0 print(avg(11)) # 10.5

    更多关于装饰器的内容可以参考:https://blog.csdn.net/John_xyz/article/details/84190920

    最新回复(0)