从变量开始

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 也将函数名看作是函数的引用。所以,可以像对待变量一样对待函数。将一个变量(函数名)赋给另一个变量,它们都指向函数所在的内存地址:
def hello():
print("Hello World\n") lianhuasheng = hello
print(id(lianhuasheng), id(hello)) >>> 21004616 21004616

由于 python 是动态解释型语言,在执行到函数定义处,即在内存中给 hello 函数开辟内存,所以这里引用 lianhuasheng 和 hello 都指向了 hello 函数的内存地址 21004616。

 
 
回过头来,再看这句话“内函数使用外函数的临时变量,外函数引用内函数的函数引用”。意思已经很明显了,内函数可以返回函数名给外函数,外函数获取该函数名,将它赋给外部变量,外部变量即成为该内函数的引用。重写改写 hello 函数为:
def inputName(name):
hostname = name + ".local"
def hello():
print(hostname)
return hello name = inputName("lianhuasheng")
print(id(name), name, name.__name__) >>> 3637576 <function inputName.<locals>.hello at 0x00378148> hello

从打印结果可以看到,引用 name “指向”的是函数名为 hello 的内存地址。

 
 
进一步的,现在需要打印 hostname, 那么我们可以通过 hello 引用来调用 hello 函数:
def inputName(name):
hostname = name + ".local"
def hello():
print(hostname)
return hello name = inputName("lianhuasheng")
print(id(name), name, name.__name__)
name() >>> 55148872 <function inputName.<locals>.hello at 0x03498148> hello
>>> lianhuasheng.local
可以看到通过闭包机制临时变量 hostname 被保存了起来(事实上是和内函数绑定在一起了),等需要调用的时候才使用临时变量的值。
 
 
类似于在函数中修改全局变量,如果在内函数中修改绑定的外部临时变量,需要使用关键字 nonlocal 显示指明该变量来自外部(外函数):
def inputName(name):
hostname = name + ".local"
def hello():
hostname = hostname + ".fullname"
print(hostname)
return hello name = inputName("lianhuasheng")
print(id(name), name, name.__name__)
name() >>> UnboundLocalError: local variable 'hostname' referenced before assignment def inputName(name):
hostname = name + ".local"
def hello():
nonlocal hostname
hostname = hostname + ".fullname"
print(hostname)
return hello name = inputName("lianhuasheng")
print(id(name), name, name.__name__)
name() >>> 46760264 <function inputName.<locals>.hello at 0x02C98148> hello
lianhuasheng.local.fullname
 

从闭包到装饰器

前面在演示闭包的时候,修改了 hello 函数,那么能否在不需要修改 hello 函数的情况下实现闭包呢?
可以的,可以使用装饰器来实现这一功能。顾名思义,装饰器是起装饰作用的东西,它并不改动装饰体的内容。给 hello 函数加个装饰器,如下:
def hello():
print("Hello World") def helloDecorator(func):
print("This is a demo of decorator")
def wrapper(*args, **kw):
return func(*args, **kw)
return wrapper lianhuasheng = helloDecorator(hello)
print(lianhuasheng.__name__) >>> This is a demo of decorator
>>> wrapper
 
通过向 helloDecorator 函数传入函数名 hello 来调用 hello 函数,实际的 hello 函数并未改动。
值得注意的是,引用 lianhuasheng “指向”的函数是 wrapper 函数,所以它的函数名是 wrapper。
 
对于这句 lianhuasheng = helloDecorator(hello) 也可将它写成 hello = helloDecorator(hello),python 在函数定义处加上 @helloDecorator 来表示这条语句,即 helloDecorator 是个装饰器。
def helloDecorator(func):
print("This is a demo of decorator")
def wrapper(*args, **kw):
return func(*args, **kw)
return wrapper @helloDecorator
def hello():
print("Hello World") print(hello.__name__) >>> This is a demo of decorator
>>> wrapper
注意引用 hello 的函数名是 wrapper!
类似的还有带参数的装饰器,这里不介绍了。
 

装饰器在类里是什么样呢?

装饰器可以用在函数中。同样的,它也可以用在类里。在类中的装饰器叫做静态方法和类成员方法。
 
静态方法和类成员方法:
class Demo:
name = "None" def __init__(self):
self.name = Demo.name
print("A demo of staticmethod and classmethod") @staticmethod
def printName(name):
print("My name is {}".format(name)) @classmethod
def inputName(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 staticmethod and classmethod
My name is lianhuasheng
<class '__main__.Demo'>
None lianhuasheng <__main__.Demo object at 0x00AF4E80> <class '__main__.Demo'>
lianhua lianhuasheng <__main__.Demo object at 0x00AF4E80> <class '__main__.Demo'>
My name is lianhuasheng
<class '__main__.Demo'>
lianhua lianhuasheng <__main__.Demo object at 0x00AF4E80> <class '__main__.Demo'>
My name is lianhuasheng
My name is lianhuasheng
 
从上例可以看出:
  • 类中,在定义前分别加上 @staticmethod 和 @classmethod 表示静态方法和类成员方法。
  • 不管是静态方法和类成员方法都能被类实例和类访问。
  • 静态方法不能修改类变量和类实例变量,且它接受的参数非 self /非 cls。相当于是定义在类中的函数。
  • 类成员方法可以修改类变量,但是不能访问类实例变量。它传入的 cls 参数实际上是类,在上例中是 <class '__main__.Demo'>。
  • 修改类实例变量的值并不会改变类变量,同样的修改类变量也不会改变类实例变量的值。
 
 
 
(完)

Python 学习笔记: 从变量到装饰器的更多相关文章

  1. python学习笔记(5)--迭代器,生成器,装饰器,常用模块,序列化

    生成器 在Python中,一边循环一边计算的机制,称为生成器:generator. 如: >>> g = (x * x for xin range(10)) >>> ...

  2. python学习笔记-(八)装饰器、生成器&迭代器

    本节课程内容概览: 1.装饰器 2.列表生成式&迭代器&生成器 3.json&pickle数据序列化 1. 装饰器 1.1 定义: 本质上是个函数,功能是装饰其他函数—就是为其 ...

  3. Python学习笔记(yield与装饰器)

    yeild:返回一个生成器对象: 装饰器:本身是一个函数,函数目的装饰其他函数(调用其他函数) 功能:增强被装饰函数的功能 装饰器一般接受一个函数对象作为参数,以便对其增强 @原函数名  来调用其他函 ...

  4. python 基础学习笔记(8)--装饰器

    **装饰器** - [ ] 装饰器和闭包有很大的联系.有时你需要在不改变源代码的情况下修改已经存在的函数.装饰器的运用可以提高效率,减少重复的代码. - [ ] 装饰器的实质是一个函数.它把一个函数作 ...

  5. python学习总结---函数使用 and 装饰器

    # 函数使用 ### 零碎知识 - 灵活的if-else ```python a = 3 if False else 5 print(a) ''' if False: a = 3 else: a = ...

  6. Python学习基础(三)——装饰器,列表生成器,斐波那契数列

    装饰器——闭包 # 装饰器 闭包 ''' 如果一个内部函数对外部(非全局)的变量进行了引用,那么内部函数被认为是闭包 闭包 = 函数块 + 定义时的函数环境 ''' def f(): x = 100 ...

  7. python学习之路 六 :装饰器

    本节重点: 掌握装饰器相关知识 ​ python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函 ...

  8. Python高级笔记(十一)装饰器【面试】

    1. 需求 开发封闭原则:虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被拓展,即: 封闭:已实现的功能代码块 开发:对拓展开发 2. ...

  9. Python学习日记(七)——装饰器

    1.必备知识 #### 一 #### def foo(): print 'foo' foo #表示是函数 foo() #表示执行foo函数 #### 二 #### def foo(): print ' ...

  10. JavaScript学习笔记(四十四) 装饰器

    装饰器模式(Decorator) 在装饰器模式中,可以在运行时给一个对象动态的添加额外的功能.当和静态类打交道的时候(static classes),这可能是一个挑战.但在JavaScript中,对象 ...

随机推荐

  1. ElasticSearch安装中文分词器(插件)、分词测试

    https://github.com/medcl/elasticsearch-analysis-ik 分词测试: https://www.elastic.co/guide/en/elasticsear ...

  2. 创建一个双模式跨运行时的 JavaScript 包

    本文将指导你发布双模式.跨运行时的 JavaScript 包.了解如何创建与 ESM 和 CommonJS 以及 Node.js.Deno 和浏览器等不同运行时兼容的库. 随着 JavaScript ...

  3. C#Socket编程详解(一)TCP与UDP简介

    一.TCP与UDP(转载) 1.TCP 1.1 定义 TCP(TransmissionControl Protocol)传输控制协议. 是一种可靠的.面向连接的协议(eg:打电话).传输效率低全双工通 ...

  4. --{module_name}_binary_host_mirror和--{module_name}_binary_site

    --{module_name}_binary_host_mirror和--{module_name}_binary_site demo // .npmrc文件 sass_binary_site=htt ...

  5. 痞子衡嵌入式:Farewell, 我的写博故事2023

    -- 题图:苏州虎丘塔 2023 年的最后一天,照旧写个年终总结.昨晚和同门师兄弟一起吃饭,有个师弟告诉痞子衡,微博上一个拥有 22.3W 粉丝的嵌入式同行今年 4 月发过一个吐槽微博,说恩智浦 MC ...

  6. Ubuntu基线指导手册

    Ubuntu基线指导手册 1.   身份鉴别策略组检测 准备: 安装一个PAM模块来启用cracklib支持,这可以提供额外的密码检查功能. 在Debian,Ubuntu或者Linux Mint使用命 ...

  7. DVWA SQL Injection(blind)(SQL盲注)全等级

    SQL Injection(blind)(盲注) 目录: SQL Injection(blind)(盲注) 1. Low 2.Medium 3.High 4.Impossible 5.运用sqlmap ...

  8. java中final关键字的使用

              1 :在java中final可以修饰类,方法,变量(包括成员变量和局部变量) 第一点:修饰类 特点:修饰的类不能被继承而且成员变量也是可以根据自己需要设置fianl 但final类 ...

  9. Python实用技巧:将 Excel转为PDF

    将Excel文件转换为PDF可以方便储存表格数据,此外在打印或共享文档时也能确保表格样式布局等在不同设备和操作系统上保持一致.今天给大家分享一个使用第三方Python库Spire.XLS for Py ...

  10. Mysql开发实践:error while loading shared libraries: libaio解决方案

    摘要:Mysql出现问题:error while loading shared libraries: libaio解决方案. 本文分享自华为云社区<Mysql出现问题:error while l ...