Python3.x的nonlocal、嵌套函数、和闭包

nonlocal作为关键字在Python3.0中被引入


nonlocal关键字的使用

nonlocal关键字,和global一样用来标识变量所在的命名空间,用在嵌套函数中,表示外层

使用nonlocal关键字可以修改外层函数中变量的值

>>> def outer():
        a = 1
        def inner():
            a += 1
            print("Inner",a)
        inner()
        print("Outer",a)
>>> outer()
UnboundLocalError: local variable 'a' referenced before
assignment
>>> def outer():
        a = 1
        def inner():
            nonlocal a
            a += 1
            print("Inner",a)
        inner()

>>> outer()
Inner 2
Outer 2

在Python2.x中实现nonlocal

与全局共享状态

>>> a = 1
>>> def outer():
        global a
        a += 1
        def inner():
            a += 1
            print("Inner",a)
        inner()
        print("Outer",a)
>>> outer()
Inner 3
Outer 3

使用类的状态

>>> class Counter(object):

        def __init__(start):
            self.state = start

        def __call__(self, label):
            print(label, self.state)
            self.state += 1
>>> c1 = Counter(1)
>>> c1("Apple")
Apple 1
>>> c1("Pear")
pear 2
>>> c2 = Counter(100)
>>> c2("Peach")
Peach 100

使用函数属性的状态

>>> def tester():
        def nested():
            print(label,nested.state)
            nested.state += 1
            nested.state = start
            return nested
>>> f= tester(0)
>>> f('span')
span 0

嵌套函数和闭包

Python语言的一大特色就是允许定义嵌套函数:定义在函数内的函数,可以访问其外层函数的变量,前面讲过使用nonlocal关键字还可以对其进行修改

闭包的定义:闭包也称词法闭包——如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure),这里说的作用域就是nonlocal;通俗来讲,闭包就是把一个函数(方法)作为一个变量来使用

使用嵌套函数可以很容易的实现闭包功能

一个简单的计数器

#!/usr/bin/env python3
#Filename: counter.py
#Function: Use closure to create a counter function that can count the number.

>>> def counter():
        i = 0
        def nested():
            nonlocal i
            i += 1
            return i
        return nested

>>> c = counter()
>>> print(c(),c(),c(),end=" ")
1 2 3
>>> d = counter()
>>> print(d(),d(),d(),end=" ")
1 2 3

闭包实现装饰器功能

装饰器(decorator)可以通过闭包实现

把被装饰得函数传入装饰器以获得装饰

#Function: The class that to be decorated.

>>>class Coordinate(object):

...     def __init__(self, x, y):
            self.x = x
            self.y = y

...     def __repr__(self):
            return "Coord: "+str(self.__dict__)

>>>     def add(a, b):
            return Coordinate(a.x + b.x, a.y + b.y)

>>>     def sub(a, b):
            return Coordinate(a.x - b.x, a.y - b.y)

>>> one = Coordinate(100, 200)
>>> two = Coordinate(300, 400)
>>> add(one, two)
Coord: {'y': 400, 'x': 400}
>>> sub(one, two)
Coord: {'y': -200, 'x': -200}   #这里出现了负值,我们可以通过装饰器修饰之使它恒为正数,详见后文
#Function: As a decorator, to insure that the results of add() and sub() are always positive.

>>> def wrapper(func):

        def checker(a, b):
            if a.x < 0 or a.y < 0:
                a = Coordinate(a.x if a.x > 0 else 0, a.y if a.y > 0 else 0)
            if b.x < 0 or b.y <0
                b = Coordinate(b.x if b.x > 0 else 0, b.y if b.y > 0 else 0)
            if ret.x < 0 or ret.y < 0:
                ret = Coordinate(ret.x if ret.x > 0 else 0, ret.y if ret.y > 0 else 0)
            return ret
        return checker

>>> add = wrapper(add)
>>> sub = wrapper(sub)
>>> add(one, two)
Coord: {'y': 400, 'x': 400}
>>> sub(one, two)
Coord: {'y': 0, 'x': 0}     

Python2.4中引入了一个符号@表示装饰器,是一个方便的语法糖

>>> @wrapper
... def add(a, b):
... return Coordinate(a.x + b.x, a.y + b.y)

上面的@wrapper等价于add = wrapper(add),写法更优雅

你可能感兴趣的:(Python,语言)