zoukankan      html  css  js  c++  java
  • 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 也将函数名看作是函数的引用。所以,可以像对待变量一样对待函数。将一个变量(函数名)赋给另一个变量,它们都指向函数所在的内存地址:
    def hello():
        print("Hello World
    ")
     
    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'>。
    • 修改类实例变量的值并不会改变类变量,同样的修改类变量也不会改变类实例变量的值。
     
     
     
    (完)
  • 相关阅读:
    ubuntu: no module named _sqlite
    mysql慢查询分析工具 pt-query-digest
    vue中的时间修饰符stop,self
    面试题 —— Ajax的基本原理总结
    es6笔记 day6-Symbol & generator
    类(class)和继承
    es6笔记 day4---模块化
    es6笔记 day3---Promise
    es6笔记 day3---对象简介语法以及对象新增
    es6笔记 day3---数组新增东西
  • 原文地址:https://www.cnblogs.com/xingzheanan/p/13040858.html
Copyright © 2011-2022 走看看