Python语言之Python函数和Python闭包
小标 2018-09-13 来源 : 阅读 618 评论 0

摘要:本文主要向大家介绍了Python语言之Python函数和Python闭包,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助。

本文主要向大家介绍了Python语言之Python函数和Python闭包,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助。

cloud.tencent.comPython functions beyond basics and a deep look at closure in Python.All examples in this article use Python 3.3+.1 Python functions beyond basicsLet's introduce several important features of Python functions first.1.1 Python function as a variablePython function, essentially is also object, like a common Python variable, can be assigned to a variable.Let's see a simple demo. First of all, define a random string. Then call print and lenfunction and pass the defined string.>>> slogan = "Life is short, I use Python"

>>> print
>>> <built-in function print>
>>> print(slogan)
>>> Life is short, I use Python

>>> len
>>> <built-in function len>
>>> len(slogan)
>>> 27These work fine, nothing special.Next step:assign print to a new variable original_printthen assign len to print.>>> original_print = print    # assign print to original_print
>>> original_print    # original_print now becomes print
>>> <built-in function print>

>>> original_print(slogan)
>>> Life is short, I use Python

>>> print = len    # assign len to print
>>> print    # print now becomes len
>>> <built-in function len>

>>> print(slogan)
>>> 27The conclusion here is straightforward: Python function can be assigned to a variable.1.2 Python function as function argumentA Python function can be passed as a argument to a function.1.2.1 Custom exampleLet's give a smart_add function as an example. It takes three arguments and the third argument is a function.def smart_add(x, y, f):
    return f(x) + f(y)Try to call smart_add and pass abs function as the third argument to it.>>> smart_add(-3, 7, abs)    # abs(-3) + abs(7)
>>> 10Again, try to pass another one: math.sqrt.>>> import math
>>> smart_add(4, 9, math.sqrt)    # math.sqrt(4) + math.sqrt(9)
>>> 5.01.2.2 Built-in example: map>>> help(map)
# ...
map(func, *iterables) --> map object
# ...According to the document: map function will make an iterator that computes the function using arguments from each of the iterables. Stops when the shortest iterable is exhausted.In short, map will take each item x in iterables and map it to func(x).x in iterables |--map to--> func(x)The first parameter of map is a function, and the second one is an iterable collection. For example, pass len function as the first argument and map string to its length.>>> names = ["Tom", "Jerry", "Bugs Bunny"]
>>> mapped_obj = map(len, names)

>>> mapped_obj
>>> <map object at 0x102629320>

>>> print(list(mapped_obj))
[3, 5, 10]Many other functions in Python are similar to map, which takes a function as argument, such as reduce, filter.You may have noticed that map function can be replaced with list comprehensions.# same effect as the map function
[func(item) for item in iterables]In fact, the reduce function was demoted from built-in in Python 2.x to the functoolsmodule in Python 3 on that account. But the map and filter functions are still built-ins in Python 3.Anyway, what we learned from this part is that Python function can be passed as an argument to a function.1.3 Return a function in a Python functionLet's define an inner function within an outer function and then return this innerfunction from outer.def outer():
    print('call outer() ...')
    # define an inner function within the outer function
    def inner():
        print('call inner() ...')
    # return the inner function 
    return innerCall the outer function and notice that the returned result is a function.>>> r = outer()    # call outer()
call outer() ...
>>> r    # Returned result by calling outer() is a function
>>> <function outer.<locals>.inner at 0x1089c6d08>
>>> r()    # Call the returned function
call inner() ...One important thing to remember is not to confuse "return a function" with "return a data value".import math

def demo_one():
    return math.sqrt    # return a function

def demo_two(x):
    return math.sqrt(x)    # return a data valueLook at another example below.# pow_later.py

def pow_later(x):
    y = 2
    def lazy_pow():
        print('calculate pow({}, {})...'.format(x, y))
        return pow(x, y)    # Use Python built-in function: pow
    return lazy_powTry it in Python shell.>>> from pow_later import pow_later

>>> my_pow = pow_later(3)
>>> my_pow
>>> <function pow_later.<locals>.lazy_pow at 0x10a043d08>pow_later returns a function that will actually calculate the result of pow(3, 2) in the future.So call it when you need, and you will get the real calculated result:>>> my_pow()
calculate pow(3, 2)...
91.4 Bonus: higher-order function and first-class functionA function that meet at least one of the following criteria is called a higher-order function.takes one or more functions as argumentsreturns a function as its resultIn fact, A Python function is not only a higher-order function, but also a first-class function, which satisfies following four criteria:can be created at runtimecan be assigned to a variablecan be passed as a argument to a functioncan be returned as the result of a function2 Python closureNow take a deeper look at the latest example mentioned above.def pow_later(x):
    y = 2
    def lazy_pow():
        print('calculate pow({}, {})...'.format(x, y))
        return pow(x, y)
    return lazy_powWe called pow_later(3) and it returned a function object.>>> my_pow = pow_later(3)
>>> my_pow
>>> <function pow_later.<locals>.lazy_pow at 0x10a043d08>then we invoked the returned function object.>>> my_pow()
calculate pow(3, 2)...
9Obviously, the variable y and the parameter x are local variables of pow_later function. So when my_pow() was called, the pow_later function had already returned, and its local variables also had gone. But in fact my_pow() still remembered the vaules of x and yeven the outer scope pow_later was long gone. How did this happen?2.1 Free variableIf a variable in a function is neither a local variable nor a parameter of that function, this variable is called a free variable of that function.In short, free variables are variables that are used locally, but defined in an enclosing scope.In our case, x is a parameter of pow_later and y is a local variable of pow_later. But within lazy_pow, x and y are free variables.2.2 Closure2.2.1 What is closureSpecifically speaking, my_pow, actually the function object returned by calling pow_later(x), is a closure.Note that the closure for lazy_pow extends the scope of lazy_pow function to include the binding for the free variables: x and y.Generally speaking, a closure is a structure (code blocks, function object, callable object, etc.) storing a function together with an environment. The environment here means information about free variables that function bounded, especially values or storage locations of free variables.For example, a closure is created, returned and assigned to my_pow after following function call.>>> my_pow = pow_later(3)Essentially, this closure is the codes of function lazy_pow together with free variables xand y.2.2.2 Inspect closureYou can see that the closure keeps names of free variables by inspecting __code__attribute of my_pow function which represents the compiled body of the function.>>> my_pow.__code__.co_freevars
>>> ('x', 'y')Meanwhile, pow_later will also keep names of local variables that are referenced by its nested functions in co_cellvars attribute of its code object.>>> pow_later.__code__.co_cellvars
>>> ('x', 'y')However, where is the values of free variables?>>> dir(my_pow)

>>> my_pow.__closure__
>>> (<cell at 0x10a428348: int object at 0x109e06b60>, <cell at 0x10a428378: int object at 0x109e06b40>)Note that my_pow has an attribute named __closure__ and it's a tuple with two elements.>>> dir(my_pow.__closure__[0])
>>> ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'cell_contents']

>>> my_pow.__closure__[0].cell_contents
>>> 3

>>> my_pow.__closure__[1].cell_contents
>>> 2So __closure__ is a tuple of cells that contain bounded values of free variables.If your Python version is 3.3+, you can use inspect module to inspect. The nonlocalsdictionary in inspecting result is exactly the bounded free variables and their values.>>> import inspect
>>> inspect.getclosurevars(my_pow)
ClosureVars(nonlocals={'x': 3, 'y': 2}, globals={}, builtins={'print': <built-in function print>, 'pow': <built-in function pow>, 'format': <built-in function format>}, unbound=set())2.2.3 __closure__Functions without free variables are not closures.def f(x):
    def g():
        pass
    return gNote that returned function g has no free variable. And its __closure__ is None.>>> h=f(1)
>>> h
>>> <function f.<locals>.g at 0x10f650158>
>>> h.__code__.co_freevars
>>> ()

>>> print(h.__closure__)
>>> NoneGlobal variables are not free variables in Python. So global functions are not closures.>>> data=200    # global
>>> def d():    # global
>>>     print(data)
... 
... 
>>> d()
>>> 200

>>> d.__code__.co_freevars
>>> ()

>>> print(d.__closure__)
>>> None__closure__ attribute of global functions is None.2.2.4 nonlocal declarationLet's review our pow_later(x) function.pass a number x to function pow_later;pow_later will return a function object;the returned function object my_pow will calculate x**2 (y=2) each time it is called.Now I'd like to change above behavior, let y increase 1 automatically each time my_pow is called. That is:the firt time call, calculate x**2;the second time call, calculate x**3;the third time call, calculate x**4;....The updated source codes are as follows.# pow_later.py

def pow_later(x):
    y = 2
    def lazy_pow():
        print('calculate pow({}, {})...'.format(x, y))
        result = pow(x, y)
        y = y + 1      # increase y
        return result
    return lazy_powTry it in Python shell.>>> from pow_later import pow_later
>>> my_pow = pow_later(3)
>>> my_pow 
>>> <function pow_later.<locals>.lazy_pow at 0x108e020d0>So far so good, let's call my_pow to see result.>>> my_pow()
>>> Traceback (most recent call last):
...
UnboundLocalError: local variable 'y' referenced before assignmentThe error message is clear enough.It's a UnboundLocalErrory is a local variablelocal variable y referenced before assignmentThe problem happens in this line: y = y + 1.We are actually assigning to y in lazy_pow scope, and that makes y becomes local to lazy_pow scope. So Python considers y a local variable of lazy_pow. Before assigning to that local variable, Python will first read the local variable y. But y is a free variable as mentioned eariler and there is no local variable named y in lazy_pow scope at all.You may think, OK, we don't assign! How about use y += 1 instead of y = y + 1? The +=operation is performed in-place, meaning that rather than creating and assigning a new value to the variable, the old variable is modified instead.The answer is: no change here. Because y is a number, which is an immutable type. +=will also create a new number object with new value behind the scene and assign the reference of the new object to y.To deal with this situation, a nonlocal declaration was introduced in Python 3. It marks a variable as a free variable even though it is assigned a new value within the function.# pow_later.py

def pow_later(x):
    y = 2
    def lazy_pow():
        nonlocal y    # nonlocal declaration
        print('calculate pow({}, {})...'.format(x, y))
        result = pow(x, y)
        y = y + 1
        return result
    return lazy_powNow the closure works well.>>> from pow_later import pow_later
>>> my_pow = pow_later(3)
>>> my_pow()
>>> calculate pow(3, 2)...
9
>>> my_pow()
>>> calculate pow(3, 3)...
27
>>> my_pow()
>>> calculate pow(3, 4)...
813 SummaryTwo topics were discussed in this article.First, Python functions are first-class functions.Second, what is closure and how it works in Python.    

以上就介绍了Python的相关知识,希望对Python有兴趣的朋友有所帮助。了解更多内容,请关注职坐标编程语言Python频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程