python绘制荷花_Python 学习笔记: 从变量到装饰器
从变量开始python 中全局变量在函数作用域内只能读,不能“写”。如果要在函数作用域内实现修改全局变量值操作,需要使用关键字 global 显示指明该变量是全局变量。但是,在函数中的变量是即时的,调用的时候才被用到,调用完变量就会销毁。变量是临时的,状态不能保存。那么,如果想保存临时变量的值,等需要用的时候才使用该变量,该怎么做呢?python 引入一种称为“闭包”的机制来处理这种情况。所谓闭包
从变量开始
python 中全局变量在函数作用域内只能读,不能“写”。如果要在函数作用域内实现修改全局变量值操作,需要使用关键字 global 显示指明该变量是全局变量。
但是,在函数中的变量是即时的,调用的时候才被用到,调用完变量就会销毁。变量是临时的,状态不能保存。
那么,如果想保存临时变量的值,等需要用的时候才使用该变量,该怎么做呢?
python 引入一种称为“闭包”的机制来处理这种情况。
所谓闭包,既是函数的嵌套,在一个函数(外函数)中嵌套另一个函数(内函数)。内函数使用外函数的临时变量,外函数引用内函数的函数引用。
让我们放下这句看起来很玄的话,先看看为什么 python 可以实现这种函数闭包机制:
python 将一切都视为对象,变量,函数,类等都是对象。简单的赋值操作,即将对象和变量名绑定在一起,变量名是对象的引用。比如,对于变量 1 来说,它可以赋值给 a ,也可以赋值给 b。那么, a 和 b 就是 1 的引用,类似于 C/C++ 中的指针,它们都“指向”变量 1 所在的内存地址:
>>> a = b = 1
>>> print(id(a), id(b))265086896 265086896
同样的,python 也将函数名看作是函数的引用。所以,可以像对待变量一样对待函数。将一个变量(函数名)赋给另一个变量,它们都指向函数所在的内存地址:
defhello():print("Hello World\n")
lianhuasheng=helloprint(id(lianhuasheng), id(hello))>>> 21004616 21004616
由于 python 是动态解释型语言,在执行到函数定义处,即在内存中给 hello 函数开辟内存,所以这里引用 lianhuasheng 和 hello 都指向了 hello 函数的内存地址 21004616。
回过头来,再看这句话“内函数使用外函数的临时变量,外函数引用内函数的函数引用”。意思已经很明显了,内函数可以返回函数名给外函数,外函数获取该函数名,将它赋给外部变量,外部变量即成为该内函数的引用。重写改写 hello 函数为:
definputName(name):
hostname= name + ".local"
defhello():print(hostname)returnhello
name= inputName("lianhuasheng")print(id(name), name, name.__name__)>>> 3637576 .hello at 0x00378148> hello
从打印结果可以看到,引用 name “指向”的是函数名为 hello 的内存地址。
进一步的,现在需要打印 hostname, 那么我们可以通过 hello 引用来调用 hello 函数:
definputName(name):
hostname= name + ".local"
defhello():print(hostname)returnhello
name= inputName("lianhuasheng")print(id(name), name, name.__name__)
name()>>> 55148872 .hello at 0x03498148>hello>>> lianhuasheng.local
可以看到通过闭包机制临时变量 hostname 被保存了起来(事实上是和内函数绑定在一起了),等需要调用的时候才使用临时变量的值。
类似于在函数中修改全局变量,如果在内函数中修改绑定的外部临时变量,需要使用关键字 nonlocal 显示指明该变量来自外部(外函数):
definputName(name):
hostname= name + ".local"
defhello():
hostname= hostname + ".fullname"
print(hostname)returnhello
name= inputName("lianhuasheng")print(id(name), name, name.__name__)
name()>>> UnboundLocalError: local variable 'hostname'referenced before assignmentdefinputName(name):
hostname= name + ".local"
defhello():
nonlocal hostname
hostname= hostname + ".fullname"
print(hostname)returnhello
name= inputName("lianhuasheng")print(id(name), name, name.__name__)
name()>>> 46760264 .hello at 0x02C98148>hello
lianhuasheng.local.fullname
从闭包到装饰器
前面在演示闭包的时候,修改了 hello 函数,那么能否在不需要修改 hello 函数的情况下实现闭包呢?
可以的,可以使用装饰器来实现这一功能。顾名思义,装饰器是起装饰作用的东西,它并不改动装饰体的内容。给 hello 函数加个装饰器,如下:
defhello():print("Hello World")defhelloDecorator(func):print("This is a demo of decorator")def wrapper(*args, **kw):return func(*args, **kw)returnwrapper
lianhuasheng=helloDecorator(hello)print(lianhuasheng.__name__)>>> This isa demo of decorator>>> wrapper
通过向 helloDecorator 函数传入函数名 hello 来调用 hello 函数,实际的 hello 函数并未改动。
值得注意的是,引用 lianhuasheng “指向”的函数是 wrapper 函数,所以它的函数名是 wrapper。
对于这句 lianhuasheng = helloDecorator(hello) 也可将它写成 hello = helloDecorator(hello),python 在函数定义处加上 @helloDecorator 来表示这条语句,即 helloDecorator 是个装饰器。
defhelloDecorator(func):print("This is a demo of decorator")def wrapper(*args, **kw):return func(*args, **kw)returnwrapper
@helloDecoratordefhello():print("Hello World")print(hello.__name__)>>> This isa demo of decorator>>> wrapper
注意引用 hello 的函数名是 wrapper!
类似的还有带参数的装饰器,这里不介绍了。
装饰器在类里是什么样呢?
装饰器可以用在函数中。同样的,它也可以用在类里。在类中的装饰器叫做静态方法和类成员方法。
静态方法和类成员方法:
classDemo:
name= "None"
def __init__(self):
self.name=Demo.nameprint("A demo of staticmethod and classmethod")
@staticmethoddefprintName(name):print("My name is {}".format(name))
@classmethoddefinputName(cls, name):
cls.name=name
Demo.printName(cls.name)print(cls)
student=Demo()
student.inputName("lianhuasheng")print(student.name, Demo.name, student, Demo)
student.name= "lianhua"
print(student.name, Demo.name, student, Demo)
Demo.inputName("lianhuasheng")print(student.name, Demo.name, student, Demo)
student.printName("lianhuasheng")
Demo.printName("lianhuasheng")>>>A demo of staticmethodandclassmethod
My nameislianhuashengNone lianhuasheng<__main__.demo object at> lianhua lianhuasheng<__main__.demo object at> My nameislianhuashenglianhua lianhuasheng<__main__.demo object at> My nameislianhuasheng
My nameis lianhuasheng
从上例可以看出:
类中,在定义前分别加上 @staticmethod 和 @classmethod 表示静态方法和类成员方法。
不管是静态方法和类成员方法都能被类实例和类访问。
静态方法不能修改类变量和类实例变量,且它接受的参数非 self /非 cls。相当于是定义在类中的函数。
类成员方法可以修改类变量,但是不能访问类实例变量。它传入的 cls 参数实际上是类,在上例中是 。
修改类实例变量的值并不会改变类变量,同样的修改类变量也不会改变类实例变量的值。
(完)
更多推荐
所有评论(0)