Python基础-28-提高

1. 浅拷贝、深拷贝

1.1 浅拷贝

  • 浅拷贝是对于一个对象的顶层拷贝
  • 通俗的理解是:拷贝了引用,并没有拷贝内容

1.2 深拷贝

  • 深拷贝是对于一个对象所有层次的拷贝(递归)

1.3 注意点

  • 浅拷贝对不可变类型和可变类型的copy不同
  • copy.copy对于可变类型,会进行浅拷贝
  • copy.copy对于不可变类型,不会拷贝,仅仅是指向

2. 私有化

  • xx: 公有变量
  • _x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问
  • __xx:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到)
  • __xx__:双前后下划线,用户名字空间的魔法对象或属性。例如:__init__, __不要自己发明这样的名字
  • xx_:单后置下划线,用于避免与Python关键词的冲突
  • 通过name mangling(名字重整(目的就是以防子类意外重写基类的方法或者属性)如:_Class__object)机制就可以访问private了
  • 注意
  • 父类中属性名为__名字的,子类不继承子类不能访问
  • 如果在子类中向__名字赋值,那么会在子类中定义的一个与父类相同名字的属性
  • _名的变量、函数、类在使用from xxx import *时都不会被导入
#coding=utf-8

class Person(object):
    def __init__(self, name, age, taste):
        self.name = name
        self._age = age 
        self.__taste = taste

    def showperson(self):
        print(self.name)
        print(self._age)
        print(self.__taste)

    def dowork(self):
        self._work()
        self.__away()


    def _work(self):
        print('my _work')

    def __away(self):
        print('my __away')

class Student(Person):
    def construction(self, name, age, taste):
        self.name = name
        self._age = age 
        self.__taste = taste

    def showstudent(self):
        print(self.name)
        print(self._age)
        print(self.__taste)

    @staticmethod
    def testbug():
        _Bug.showbug()
        
# 模块内可以访问,当from  cur_module import *时,不导入
class _Bug(object):
    @staticmethod
    def showbug():
        print("showbug")

s1 = Student('jack'25'football')
s1.showperson()
print('*'*20)

# 无法访问__taste,导致报错
# s1.showstudent() 
s1.construction('rose'30'basketball')
s1.showperson()
print('*'*20)

s1.showstudent()
print('*'*20)

Student.testbug()

3. import导入模块

3.1 import 搜索路径

  • 从上面列出的目录里依次查找要导入的模块文件
  • '' 表示当前路径
  • 列表中的路径的先后顺序代表了python解释器在搜索模块时的先后顺序

程序执行时添加新的模块路径

sys.path.append('/home/itcast/xxx'# 追加
sys.path.insert(0'/home/itcast/xxx')  # 指定位置新增,可以确保先搜索这个路径

搜索路径如下

['/home/python/xxxx',
 '',
 '/usr/bin',
 '/usr/lib/python35.zip',
 '/usr/lib/python3.5',
 '/usr/lib/python3.5/plat-x86_64-linux-gnu',
 '/usr/lib/python3.5/lib-dynload',
 '/usr/local/lib/python3.5/dist-packages',
 '/usr/lib/python3/dist-packages',
 '/usr/lib/python3/dist-packages/IPython/extensions',
 '/home/python/.ipython']

3.2 重新导入模块

模块被导入后,import module不能重新导入模块,重新导入需用reload

4. 多继承以及MRO顺序

4.1 总结

  • super().__init__相对于类名.__init__,在单继承上用法基本无差
  • 但在多继承上有区别
  • super方法能保证每个父类的方法只会执行一次
  • 使用类名的方法会导致方法被执行多次
  • 多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错
  • 单继承时,使用super方法,则不能全部传递只能传父类方法所需的参数,否则会报错
  • 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍,而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因

4.2 单独调用父类的方法

# coding=utf-8

print("******多继承使用类名.__init__ 发生的状态******")
class Parent(object):
    def __init__(self, name):
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')

class Son1(Parent):
    def __init__(self, name, age):
        print('Son1的init开始被调用')
        self.age = age
        Parent.__init__(self, name)
        print('Son1的init结束被调用')

class Son2(Parent):
    def __init__(self, name, gender):
        print('Son2的init开始被调用')
        self.gender = gender
        Parent.__init__(self, name)
        print('Son2的init结束被调用')

class Grandson(Son1, Son2):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        Son1.__init__(self, name, age)  # 单独调用父类的初始化方法
        Son2.__init__(self, name, gender)
        print('Grandson的init结束被调用')

gs = Grandson('grandson'12'男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)

print("******多继承使用类名.__init__ 发生的状态******nn")
  • 运行结果如下
******多继承使用类名.__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用类名.__init__ 发生的状态******

4.3 多继承中super调用有所父类的被重写的方法

  • 如果2个子类中都继承了父类,当在子类中通过父类名调用时,parent被执行了2次
  • 如果2个子类中都继承了父类,当在子类中通过super调用时,parent被执行了1次
print("******多继承使用super().__init__ 发生的状态******")
class Parent(object):
    def __init__(self, name, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')

class Son1(Parent):
    def __init__(self, name, age, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son1的init开始被调用')
        self.age = age
        super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son1的init结束被调用')

class Son2(Parent):
    def __init__(self, name, gender, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son2的init开始被调用')
        self.gender = gender
        super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son2的init结束被调用')

class Grandson(Son1, Son2):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
        # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
        # super(Grandson, self).__init__(name, age, gender)
        super().__init__(name, age, gender)
        print('Grandson的init结束被调用')

print(Grandson.__mro__)

gs = Grandson('grandson'12'男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("******多继承使用super().__init__ 发生的状态******nn")
  • 运行结果:
******多继承使用super().__init__ 发生的状态******
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandsoninit开始被调用
Son1init开始被调用
Son2init开始被调用
parentinit开始被调用
parentinit结束被调用
Son2init结束被调用
Son1init结束被调用
Grandsoninit结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用super().__init__ 发生的状态******

4.4 单继承中super

print("******单继承使用super().__init__ 发生的状态******")
class Parent(object):
    def __init__(self, name):
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')

class Son1(Parent):
    def __init__(self, name, age):
        print('Son1的init开始被调用')
        self.age = age
        super().__init__(name)  # 单继承不能提供全部参数
        print('Son1的init结束被调用')

class Grandson(Son1):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        super().__init__(name, age)  # 单继承不能提供全部参数
        print('Grandson的init结束被调用')

gs = Grandson('grandson'12'男')
print('姓名:', gs.name)
print('年龄:', gs.age)
#print('性别:', gs.gender)
print("******单继承使用super().__init__ 发生的状态******nn")

5. property属性

  • 一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法
  • 把方法,像属性那样使用
  • 定义时,在实例方法的基础上添加 **@property **** 装饰器 ;并且仅有一个self参数**
  • 调用时,无需括号
  • 方法:foo_obj.func()
  • property属性:foo_obj.prop
  • property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回
# ############### 定义 ###############
class Foo:
    def func(self):
        pass

    # 定义property属性
    @property
    def prop(self):
        pass

# ############### 调用 ###############
foo_obj = Foo()
foo_obj.func()  # 调用实例方法
foo_obj.prop  # 调用property属性

5.1 property属性的有两种方式

  • 装饰器 即:在方法上应用装饰器
  • 类属性 即:在类中定义值为property对象的类属性

装饰器方式

  • 在类的实例方法上应用@property装饰器
  • Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。(如果类继object,那么该类是新式类)

经典类,具有一种@property装饰器

  • 经典类中的属性只有一种访问方式,其对应被 @property  修饰的方法
# ############### 定义 ###############    
class Goods:
    @property
    def price(self):
        return "laowang"
# ############### 调用 ###############
obj = Goods()
result = obj.price  # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
print(result)

新式类,具有三种@property装饰器

  • 新式类中的属性有三种访问方式,并分别对应了三个被@property@方法名.setter@方法名.deleter修饰的方法
#coding=utf-8
# ############### 定义 ###############
class Goods:
    """python3中默认继承object类
        以python2、3执行此程序的结果不同,因为只有在python3中才有@xxx.setter  @xxx.deleter
    """

    @property
    def price(self):
        print('@property')

    @price.setter
    def price(self, value):
        print('@price.setter')

    @price.deleter
    def price(self):
        print('@price.deleter')

# ############### 调用 ###############
obj = Goods()
obj.price          # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
obj.price = 123    # 自动执行 @price.setter 修饰的 price 方法,并将  123 赋值给方法的参数
del obj.price      # 自动执行 @price.deleter 修饰的 price 方法
  • 由于新式类中具有三种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
del obj.price     # 删除商品原价

类属性方式,创建值为property对象的类属性

  • 当使用类属性的方式创建property属性时,经典类新式类无区别
class Foo:
    def get_bar(self):
        return 'laowang'

    BAR = property(get_bar)

obj = Foo()
reuslt = obj.BAR  # 自动调用get_bar方法,并获取方法的返回值
print(reuslt)
  • property方法中有个四个参数
  • 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
  • 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
  • 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
  • 第四个参数是字符串,调用 对象.属性.__doc__,此参数是该属性的描述信息
#coding=utf-8
class Foo(object):
    def get_bar(self):
        print("getter...")
        return 'laowang'

    def set_bar(self, value): 
        """必须两个参数"""
        print("setter...")
        return 'set value' + value

    def del_bar(self):
        print("deleter...")
        return 'laowang'

    BAR = property(get_bar, set_bar, del_bar, "description...")

obj = Foo()

obj.BAR  # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex"  # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
desc = Foo.BAR.__doc__  # 自动获取第四个参数中设置的值:description...
print(desc)
del obj.BAR  # 自动调用第三个参数中定义的方法:del_bar方法
  • 由于类属性方式创建property属性具有3种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    def get_price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    def set_price(self, value):
        self.original_price = value

    def del_price(self):
        del self.original_price

    PRICE = property(get_price, set_price, del_price, '价格属性描述...')

obj = Goods()
obj.PRICE         # 获取商品价格
obj.PRICE = 200   # 修改商品原价
del obj.PRICE     # 删除商品原价

5.2 property属性-应用

私有属性添加getter和setter方法

class Money(object):
    def __init__(self):
        self.__money = 0

    def getMoney(self):
        return self.__money

    def setMoney(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")

使用property升级getter和setter方法

class Money(object):
    def __init__(self):
        self.__money = 0

    def getMoney(self):
        return self.__money

    def setMoney(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")

    # 定义一个属性,当对这个money设置值时调用setMoney,当获取值时调用getMoney
    money = property(getMoney, setMoney)  

a = Money()
a.money = 100  # 调用setMoney方法
print(a.money)  # 调用getMoney方法
#100

使用property取代getter和setter方法

  • 重新实现一个属性的设置和读取方法,可做边界判定
class Money(object):
    def __init__(self):
        self.__money = 0

    # 使用装饰器对money进行装饰,那么会自动添加一个叫money的属性,当调用获取money的值时,调用装饰的方法
    @property
    def money(self):
        return self.__money

    # 使用装饰器对money进行装饰,当对money设置值时,调用装饰的方法
    @money.setter
    def money(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")

a = Money()
a.money = 100
print(a.money)

6. 魔法属性

6.1__doc__

  • 表示类的描述信息
class Foo:
    """ 描述类信息,这是用于看片的神奇 """
    def func(self):
        pass

print(Foo.__doc__)
#输出:类的描述信息

6.2 __module____class__

  • __module__ 表示当前操作的对象在那个模块
  • __class__ 表示当前操作的对象的类是什么

test.py

# -*- coding:utf-8 -*-

class Person(object):
    def __init__(self):
        self.name = 'laowang'

main.py

from test import Person

obj = Person()
print(obj.__module__)  # 输出 test 即:输出模块
print(obj.__class__)  # 输出 test.Person 即:输出类

6.3__init__

  • 初始化方法,通过类创建对象时,自动触发执行
class Person:
    def __init__(self, name):
        self.name = name
        self.age = 18
obj = Person('laowang')  # 自动执行类中的 __init__ 方法

6.4__del__

  • 当对象在内存中被释放时,自动触发执行。
  • 注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,del的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo:
    def __del__(self):
        pass

6.5__call__

  • 对象后面加括号,触发执行。
  • 注:__init__方法的执行是由创建对象触发的,即:对象 = 类名()
  • 而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        print('__call__')


obj = Foo()  # 执行 __init__
obj()  # 执行 __call__

6.6 __dict__

  • 类或对象中的所有属性
  • 类的实例属性属于对象;类中的类属性和方法等属于类,即:
class Province(object):
    country = 'China'

    def __init__(self, name, count):
        self.name = name
        self.count = count

    def func(self, *args, **kwargs):
        print('func')

# 获取类的属性,即:类属性、方法、
print(Province.__dict__)
# 输出:{'__dict__': <attribute '__dict__' of 'Province' objects>, '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Province' objects>, 'func': <function Province.func at 0x101897950>, '__init__': <function Province.__init__ at 0x1018978c8>}

obj1 = Province('山东'10000)
print(obj1.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 10000, 'name': '山东'}

obj2 = Province('山西'20000)
print(obj2.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 20000, 'name': '山西'}

6.7 __str__

  • 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
class Foo:
    def __str__(self):
        return 'laowang'


obj = Foo()
print(obj)
# 输出:laowang

6.8 __getitem____setitem____delitem__

  • 用于索引操作,如字典。以上分别表示获取、设置、删除数据
# -*- coding:utf-8 -*-

class Foo(object):

    def __getitem__(self, key):
        print('__getitem__', key)

    def __setitem__(self, key, value):
        print('__setitem__', key, value)

    def __delitem__(self, key):
        print('__delitem__', key)


obj = Foo()

result = obj['k1']      # 自动触发执行 __getitem__
obj['k2'] = 'laowang'   # 自动触发执行 __setitem__
del obj['k1']           # 自动触发执行 __delitem__

6.9 __getslice____setslice____delslice__

  • 该三个方法用于分片操作,如:列表
# -*- coding:utf-8 -*-

class Foo(object):

    def __getslice__(self, i, j):
        print('__getslice__', i, j)

    def __setslice__(self, i, j, sequence):
        print('__setslice__', i, j)

    def __delslice__(self, i, j):
        print('__delslice__', i, j)

obj = Foo()

obj[-1:1]                   # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44]    # 自动触发执行 __setslice__
del obj[0:2]                # 自动触发执行 __delslice__

7. with与上下文管理器

7.1 with

  • 普通版
def m1():
    f = open("output.txt""w")
    f.write("python之禅")
    f.close()
  • 进阶版
def m2():
    f = open("output.txt""w")
    try:
        f.write("python之禅")
    except IOError:
        print("oops error")
    finally:
        f.close()
  • 高级版
def m3():
    with open("output.txt""r"as f:
        f.write("Python之禅")
  • 一种更加简洁、优雅的方式就是用 with 关键字。
  • open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的.

7.2 上下文管理器

  • 任何实现了 __enter__()__exit__()方法的对象都可称之为上下文管理器
  • 上下文管理器对象可以使用 with 关键字。显然,文件(file)对象也实现了上下文管理器。

模拟实现一个自己的文件类,让该类实现__enter__()__exit__() 方法

class File():

    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        print("entering")
        self.f = open(self.filename, self.mode)
        return self.f

    def __exit__(self, *args):
        print("will exit")
        self.f.close()
  • __enter__()方法返回资源对象,这里就是你将要打开的那个文件对象
  • __exit__() 方法处理一些清除工作。
  • 因为 File 类实现了上下文管理器,现在就可以使用 with 语句了。
with File('out.txt''w'as f:
    print("writing")
    f.write('hello, python')

实现上下文管理器的另外方式

  • Python 还提供了一个 contextmanager装饰器,更进一步简化了上下文管理器的实现方式。
  • 通过yield 将函数分割成两部分yield 之前的语句__enter__方法中执行,yield 之后的语句__exit__方法中执行。紧跟在 yield 后面的值是函数的返回值
  • 定义一个具有上下文的类
from contextlib import contextmanager

@contextmanager
def my_open(path, mode):
    f = open(path, mode)
    yield f
    f.close()
  • 调用
with my_open('out.txt''w'as f:
    f.write("hello , the simplest context manager")

8. 闭包

8.1 闭包是一个函数

  • 为什么使用闭包
  • 由于函数作用域的限制,在函数外,无法使用函数内的变量。
  • 如果想使用函数内的变量,可以通过闭包来实现。
  • 闭包就是,在一个函数A内创建一个函数B,函数B操作函数A内的变量,返回函数B。当调用函数A的时候,会返回函数B,此时返回值是一个函数。然后再次按照函数方式调用返回值即可。
  • 注意:由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
  • 定义:在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包

8.2 闭包的构成条件

  • 通过闭包的定义,我们可以得知闭包的形成条件:
  • 在函数嵌套(函数里面再定义函数)的前提下
  • 内部函数使用了外部函数的变量(还包括外部函数的参数)
  • 外部函数返回了内部函数

简单闭包的示例代码

# 定义一个外部函数
def func_out(num1):
    # 定义一个内部函数
    def func_inner(num2):
        # 内部函数使用了外部函数的变量(num1)
        result = num1 + num2
        print("结果是:", result)
    # 外部函数返回了内部函数,这里返回的内部函数就是闭包
    return func_inner

# 创建闭包实例    
f = func_out(1)
# 执行闭包
f(2)
f(3)

8.3 闭包的作用

闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。 注意点: 由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存

8.4 小结

当返回的内部函数使用了外部函数的变量就形成了闭包 闭包可以对外部函数的变量进行保存

  • 实现闭包的标准格式
# 外部函数
def test1(a):
    b = 10
    # 内部函数
    def test2():
        # 内部函数使用了外部函数的变量或者参数
        print(a, b)
        # 返回内部函数, 这里返回的内部函数就是闭包实例
    return test2

8.5 修改闭包内使用的外部变量

  • 修改闭包内使用的外部函数变量使用 nonlocal 关键字来完成
# 定义一个外部函数
def func_out(num1):

    # 定义一个内部函数
    def func_inner(num2):
        # 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1
        nonlocal num1  # 告诉解释器,此处使用的是 外部变量a
        # 修改外部变量num1
        num1 = 10
        # 内部函数使用了外部函数的变量(num1)
        result = num1 + num2
        print("结果是:", result)

    print(num1)
    func_inner(1)
    print(num1)

    # 外部函数返回了内部函数,这里返回的内部函数就是闭包
    return func_inner

# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)

9. 装饰器

9.1 理解

  • 装饰器(Decorators)是Python的一个重要部分。
  • 装饰器是修改其他函数的功能的函数。
  • 装饰器其实也就是一个函数,一个用来包装函数的函数,返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问。
  • 装饰器,当python从上向下解析的时候,会执行这个装饰器的
  • 也就是说,当python从上向下解析文件的时候,哪怕不调用被装饰的函数,但是也会执行这个装饰器的
  • 装饰器,不是调用被装饰的函数的是调用,而是解析的时候就调用这个装饰器

一切皆对象

  • Python中的函数,也是对象;
  • 可以修改函数这个对象的指向,这个是装饰器的基础
#### 第一波 ####
def foo():
    print('foo')

foo  # 表示是函数
foo()  # 表示执行foo函数

#### 第二波 ####
def foo():
    print('foo')

foo = lambda x: x + 1

foo()  # 执行lambda表达式,而不再是原来的foo函数,因为foo这个名字被重新指向了另外一个匿名函数
  • 函数名仅仅是个变量,只不过指向了定义的函数而已,所以才能通过 函数名()调用
  • 如果 函数名=xxx被修改了,那么当在执行 **函数名()**时,调用的就不知之前的那个函数了

9.2 在函数中定义函数

  • 在函数外无法调用函数内的函数
#!/usr/bin/env python
#coding:utf-8

def hi(name="yasoob"):
    print("now you are inside the hi() function")
 
    def greet():
        return"now you are in the greet() function"
 
    def welcome():
        return"now you are in the welcome() function"
 
    print(greet())
    print(welcome())
    print("now you are back in the hi() function")
 
hi()
#output:now you are inside the hi() function
#       now you are in the greet() function
#       now you are in the welcome() function
#       now you are back in the hi() function
 
# 上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。
# 然后greet()和welcome()函数在hi()函数之外是不能访问的,比如:
 
greet()# 在hi函数外无法访问hi函数内的函数
#outputs: NameError: name 'greet' is not defined

9.3 从函数中返回函数

  • 闭包
#!/usr/bin/env python
#coding:utf-8
def hi(name="yasoob"):
    def greet():
        return"now you are in the greet() function"

    def welcome():
        return"now you are in the welcome() function"

    if name =="yasoob":
        return greet # 这里没有加(),是因为这里是赋值,而不是去执行
    else:
        return welcome

a = hi()  #此时,a就是hi()函数的执行结果,因为hi后面有括号,代表执行hi()函数
print(a)
#outputs: <function greet at 0x7f2143c01500>

#上面清晰地展示了变量a现在指向到hi()函数中的greet()函数
#现在试试这个

print(a())  # 相当于调用的hi()()
#outputs: now you are in the greet() function

代码解释:

  • 再次看看这个代码。在if/else语句中我们返回greet和welcome,而不是greet()和welcome()。
  • 为什么那样?
  • 这是因为当你把一对小括号放在后面,这个函数就会执行
  • 然而如果你不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。
  • 当 a=hi()时,由于hi后面有小括号,所以hi()会执行,hi()的返回值赋值给变量a;由于name是默认参数,所以函数greet返回了,赋值给变量a,a就代表greet函数;
  • print(a()),就代表执行greet(),且输出greet()的返回值;

9.4 将函数作为参数传递给另一个函数(装饰器的前提)

  • 将函数作为参数传递给另一个函数A中,然后在函数A中执行接收过来的参数(函数);
  • 这也算是一个装饰器了,执行doSomethingBeforeHi(hi)方法实现了:在执行hi函数之前执行了一起其他的事情;
#!/usr/bin/env python
#coding:utf-8
 
def hi():
    return"hi yasoob!"
 
# 该方法接收个函数作为参数
def doSomethingBeforeHi(func):
    print("I am doing some boring work before executing hi()")
    print(func())
 
doSomethingBeforeHi(hi)
#outputs:I am doing some boring work before executing hi()
#        hi yasoob!

9.5 第一个装饰器

  • 装饰器也是一个函数,这个函数中还有一个函数,用于实现装饰;
  • 装饰器,当python从上向下解析的时候,会执行这个装饰器的
  • 也就是说,当python从上向下解析文件的时候,哪怕不调用被装饰的函数,但是也会执行这个装饰器的
  • 装饰器,不是调用被装饰的函数的是调用,而是解析的时候就调用这个装饰器
#!/usr/bin/env python
#coding:utf-8
 
# 装饰器,参数就是要被装饰的函数
def a_new_decorator(a_func):
    # 真正实现装饰功能
    defwrapTheFunction():
        print("执行a_func()函数之前做些什么")# 调用前装饰
        a_func()# 执行被装饰的函数
        print("执行a_func()函数之后做些什么")#调用后装饰
 
    return wrapTheFunction
 
defa_function_requiring_decoration():
    print("装饰我")
 
 
a_function_requiring_decoration()
#outputs: "装饰我"
 
# a_function_requiring_decoration()被wrapTheFunction()给包装了
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
 
a_function_requiring_decoration()
""" outputs
执行a_func()函数之前做些什么
装饰我
执行a_func()函数之后做些什么

9.6 使用@装饰器函数 来使用装饰器:语法糖

#!/usr/bin/env python
#coding:utf-8
 
def  a_new_decorator(a_func):
 
    def wrapTheFunction():
        print("执行a_func()函数之前做些什么")
 
        a_func()
 
        print("执行a_func()函数之后做些什么")
 
    return wrapTheFunction
 
# 通过@装饰器来装饰一个函数等价于
# a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
@a_new_decorator
def a_function_requiring_decoration():
    print("装饰我")
 
 
a_function_requiring_decoration()
"""
执行a_func()函数之前做些什么
装饰我
执行a_func()函数之后做些什么
"""

@方式内部实现原理

  • 1.上面的代码,Python解释器会从上向下开始解释代码;
  • 2.当执行到def a_new_decorator(a_func)的时候,会把这个函数加载到内存,然后继续向下执行;
  • 3.执行到@a_new_decorator的时候,会把其下紧跟的函数def a_function_requiring_decoration()函数作为参数传递a_new_decorator方法中去执行,然后把返回值(是一个函数)赋值a_function_requiring_decoration函数,此时已经装饰完毕,就等待调用了;
  • 4.当调用a_function_requiring_decoration函数的时候,其实调用的是步骤3返回的函数(装饰完毕的函数)。

9.7 使用工具函数来使用装饰器

#!/usr/bin/env python
#coding:utf-8
 
from functools import wraps
 
def a_new_decorator(a_func):
    @wraps(a_func)
    def wrapTheFunction():
        print("I am doing some boring work before executing a_func()")
        a_func()
        print("I am doing some boring work after executing a_func()")
    return wrapTheFunction
 
@a_new_decorator
def a_function_requiring_decoration():
    """Hey yo! Decorate me!"""
    print("I am the function which needs some decoration to "
          "remove my foul smell")
 
print(a_function_requiring_decoration.__name__)
# Output: a_function_requiring_decoration

9.8 装饰器蓝本规范

  • 注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称,注释文档,参数列表等功能。
  • 这可以让我们在装饰器里面访问在装饰之前的函数的属性。
#!/usr/bin/env python
#coding:utf-8
 
from functools import wraps
def decorator_name(f):
    @wraps(f)
    def decorated(*args,**kwargs):
        ifnot can_run:
            return"Function will not run"
        return f(*args,**kwargs)
    return decorated
 
@decorator_name
def func():
    return("Function is running")
 
can_run =True
print(func())
# Output: Function is running
 
can_run =False
print(func())

9.9 在函数中嵌入装饰器

#!/usr/bin/env python
#coding:utf-8
 
from functools import wraps
 
def logit(logfile='out.log'):
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args,**kwargs):
            log_string = func.__name__ +" was called"
            print(log_string)
            # 打开logfile,并写入内容
            with open(logfile,'a')as opened_file:
                # 现在将日志打到指定的logfile
                opened_file.write(log_string +'n')
            return func(*args,**kwargs)
        return wrapped_function
    return logging_decorator
 
@logit()
def myfunc1():
    pass
 
myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
 
@logit(logfile='func2.log')
defmyfunc2():
    pass
 
myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串

9.10 装饰器类

#!/usr/bin/env python
#coding:utf-8
 
from functools import wraps
 
class logit(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile
 
    def __call__(self, func):
        @wraps(func)
        def wrapped_function(*args,**kwargs):
            log_string = func.__name__ +" was called"
            print(log_string)
            # 打开logfile并写入
            with open(self.logfile,'a')as opened_file:
                # 现在将日志打到指定的文件
                opened_file.write(log_string +'n')
            # 现在,发送一个通知
            self.notify()
            return func(*args,**kwargs)
        return wrapped_function
 
    def notify(self):
        # logit只打日志,不做别的
        pass
  • 实现logit的子类,可以实现发送email的功能
class email_logit(logit):
    '''
    一个logit的实现版本,可以在函数调用时发送email给管理员
    '''

    def __init__(self, email='admin@myproject.com',*args,**kwargs):
        self.email = email
        super(logit, self).__init__(*args,**kwargs)
 
    def notify(self):
        # 发送一封email到self.email
        # 这里就不做实现了
        pass

9.11 给带返回值的函数装饰

  • 装饰器本身是一个函数;
  • 装饰器的功能就是在函数执行前和执行后,需要做些什么
  • 装饰器函数的实现:
  • 装饰器函数接受一个参数,这个参数实际上是要装饰的函数;
  • 装饰器函数内部又有一个内部函数,这个内部函数调用装饰器函数接受的函数;
  • 然后在内部函数内来实现一些装饰器的功能,然后内部函数返回内部函数名;
  • 装饰器可以获得原函数的返回值,然后返回。
#_*_coding:utf-8_*_

#使用函数完成一个装饰器,这个装饰器的名字定义为outer
def outer(fun): #这里的参数,是要装饰的函数名
    def wrapper(arg): #这里的参数,是要装饰的函数的参数
        print '验证'
        result = fun(arg) # 调用被装饰的参数
        return "result values: " + result
    return wrapper # 返回装饰器内部方法

@outer
def test(arg):
    print '被装饰'
    return 'test的返回值'
print(test('hello'))
  • 上述同样的方法,通过闭包来调用
#_*_coding:utf-8_*_

#使用函数完成一个装饰器,这个装饰器的名字定义为outer
def outer(fun): #这里的参数,是要装饰的函数名
    def wrapper(arg): #这里的参数,是要装饰的函数的参数
        print '验证'
        result = fun(arg) # 调用被装饰的参数
        return "result values: " + result
    return wrapper # 返回装饰器内部方法

@outer
def test(arg):
    print '被装饰'
    return 'test的返回值'

print(outer(test)) #<function wrapper at 0x00000000027B96D8>
sp = outer(test) # sp就是wrapper函数
resp = sp('测试'#这里调用wrapper函数
print resp

9.12 装饰器-没有内嵌包装函数的装饰器

#_*_coding:utf-8_*_

def deco(func):
    print('before myfunc() called')
    func()
    print('after myfunc() called')
    return func
@deco
def myfunc():
    print ('myfunc() called')
myfunc()
# 结果
#before myfunc() called
#myfunc() called
#after myfunc() called
#myfunc() called

9.13 让装饰器带参数,是装饰器的参数

  • 带有参数的装饰器,能够起到在运行时,有不同的功能。
  • 小技巧:
  • 先写不带参数的装饰器,写完了后
  • 再装饰器外面包装一层即可,参数,在第一步写好的装饰器中使用即可
#_*_coding:utf-8_*_

def deco(arg): # arg是装饰器的参数
    def _deco(func):#这里是内部的装饰器函数
        def __deco():
            print('before %s called [%s]' %(func.__name__, arg))
            func()
            print('after %s called [%s]' %(func.__name__, arg))
        return __deco
    return _deco

@deco("mymodule")
def myfunc():
    print('myfunc() called')
@deco('module2')
def myfunc2():
    print('myfunc2 called')
myfunc()
myfunc2()

#结果
#before myfunc called [mymodule]
#myfunc() called
#after myfunc called [mymodule]
#before myfunc2 called [module2]
#myfunc2 called
#after myfunc2 called [module2]

另一个例子

def set_level(level_num):
 def set_func(func):
  def call_func(*args, **kwargs):
   if level_num == 1:
    print("----权限级别1,验证----")
   elif level_num == 2:
    print("----权限级别2,验证----")
   return func()
  return call_func
 return set_func

# 带有参数的装饰器装饰过程分为2步:
# 1. 调用set_level函数,把1当做实参
# 2. set_level返回一个装饰器的引用,即set_func
# 3. 用返回的set_func对test1函数进行装饰(装饰过程与之前一样)
@set_level(1)
def test1():
 print("-----test1---")
 return "ok"

@set_level(2)
def test2():
 print("-----test2---")
 return "ok"


test1()
test2()

9.14 类当做装饰器

把对象当做方法来调用

  • 通过__call__方法来实现
# _*_coding:utf-8_*_
class Person():
    def __call__(self):
        print('__call__方法让对象可以按函数方式调用,调用的就是这个')

t = Person()
t()

类当做装饰器

# _*_coding:utf-8_*_
class Person():
    # 当类做装饰器的时候,这个类的构造函数中一定要接受一个参数
    # 这个参数就是要装饰的方法的方法名
    def __init__(self, func):
        print('--初始化--  %s' %func)
        func()
        self.__func = func
    def __call__(self):
        print('__call__方法让对象可以按函数方式调用,调用的就是这个')
        self.__func()
@Person
def hello(): # 这种写法等价于 hello = Person(hello)
    print('---hello---')

hello() # 这里调用的是Person的__call__
  • 当用Person来装作装饰器对hello函数进行装饰的时候,首先会创建Person的实例对象
  • 并且会把hello这个函数名当做参数传递到__init__方法中
  • 即在__init__方法中的func变量指向了hello函数体
  • hello函数相当于指向了用Person创建出来的实例对象
  • 当在使用hello()进行调用时,就相当于让这个对象按照函数的方式调用,因此会调用这个Person对象的__call__方法
  • 为了能够在__call__方法中调用原来hello指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
  • 所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到hello之前的函数体

10. 九步学装饰器

1. 第一步:最简单的函数,准备附加额外功能

#!/usr/bin/env python
#coding:utf-8
 
'''示例1: 最简单的函数,表示调用了两次'''
def myfunc():
    print("myfunc() called.")
 
myfunc()
myfunc()

2. 第二步:使用装饰函数在函数执行前和执行后分别附加额外功能

#!/usr/bin/env python
#coding:utf-8
 
'''示例2: 替换函数(装饰)
装饰函数的参数是被装饰的函数对象,返回原函数对象
装饰的实质语句: myfunc = deco(myfunc)'''

 
def deco(func):
    print("before myfunc() called.")
    func()
    print("after myfunc() called.")
    return func
 
def myfunc():
    print("myfunc() called.")
 
myfunc = deco(myfunc)
 
myfunc()
myfunc()
"""
before myfunc() called.
myfunc() called.
after myfunc() called.
myfunc() called.
myfunc() called.
"""

3. 第三步:使用语法糖@来装饰函数

#!/usr/bin/env python
#coding:utf-8
 
'''示例3: 使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)”
但发现新函数只在第一次被调用,且原函数多调用了一次'''

 
def deco(func):
    print("before myfunc() called.")
    func()
    print("  after myfunc() called.")
    return func
 
@deco
def myfunc():
    print(" myfunc() called.")
 
myfunc()
myfunc()
"""
before myfunc() called.
 myfunc() called.
  after myfunc() called.
 myfunc() called.
 myfunc() called.
"""

4. 第四步:使用内嵌包装函数来确保每次新函数都被调用

#!/usr/bin/env python
#coding:utf-8
 
'''示例4: 使用内嵌包装函数来确保每次新函数都被调用,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''

 
def deco(func):
    def _deco():
        print("before myfunc() called.")
        func()
        print("  after myfunc() called.")
        # 不需要返回func,实际上应返回原函数的返回值
    return _deco
 
@deco
def myfunc():
    print(" myfunc() called.")
    return 'ok'
 
myfunc()
myfunc()
"""
before myfunc() called.
 myfunc() called.
  after myfunc() called.
before myfunc() called.
 myfunc() called.
  after myfunc() called.
"""

5. 第五步:对带参数的函数进行装饰

#!/usr/bin/env python
#coding:utf-8
 
'''示例5: 对带参数的函数进行装饰,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''

 
def deco(func):
    def _deco(a, b):
        print("before myfunc() called.")
        ret = func(a, b)
        print("  after myfunc() called. result: %s" % ret)
        return ret
    return _deco
 
@deco
def myfunc(a, b):
    print(" myfunc(%s,%s) called." % (a, b))
    return a + b
 
myfunc(12)
myfunc(34)
"""
before myfunc() called.
 myfunc(1,2) called.
  after myfunc() called. result: 3
before myfunc() called.
 myfunc(3,4) called.
  after myfunc() called. result: 7
"""

6. 第六步:对参数数量不确定的函数进行装饰

#!/usr/bin/env python
#coding:utf-8
 
'''示例6: 对参数数量不确定的函数进行装饰,
参数用(*args, **kwargs),自动适应变参和命名参数'''

 
def deco(func):
    def _deco(*args, **kwargs):
        print("before %s called." % func.__name__)
        ret = func(*args, **kwargs)
        print("  after %s called. result: %s" % (func.__name__, ret))
        return ret
    return _deco
 
@deco
def myfunc(a, b):
    print(" myfunc(%s,%s) called." % (a, b))
    return a+b
 
@deco
def myfunc2(a, b, c):
    print(" myfunc2(%s,%s,%s) called." % (a, b, c))
    return a+b+c
 
myfunc(12)
myfunc(34)
myfunc2(123)
myfunc2(345)
"""
before myfunc called.
 myfunc(1,2) called.
  after myfunc called. result: 3
before myfunc called.
 myfunc(3,4) called.
  after myfunc called. result: 7
before myfunc2 called.
 myfunc2(1,2,3) called.
  after myfunc2 called. result: 6
before myfunc2 called.
 myfunc2(3,4,5) called.
  after myfunc2 called. result: 12
"""

7. 第七步:让装饰器带参数

#!/usr/bin/env python
#coding:utf-8
 
'''示例7: 在示例4的基础上,让装饰器带参数,
和上一示例相比在外层多了一层包装。
装饰函数名实际上应更有意义些'''

 
def deco(arg):
    def _deco(func):
        def __deco():
            print("before %s called [%s]." % (func.__name__, arg))
            func()
            print("  after %s called [%s]." % (func.__name__, arg))
        return __deco
    return _deco
 
@deco("mymodule")
def myfunc():
    print(" myfunc() called.")
 
@deco("module2")
def myfunc2():
    print(" myfunc2() called.")
 
myfunc()
myfunc2()
"""
before myfunc called [mymodule].
 myfunc() called.
  after myfunc called [mymodule].
before myfunc2 called [module2].
 myfunc2() called.
  after myfunc2 called [module2].
"""

8. 第八步:让装饰器带 类 参数

#!/usr/bin/env python
#coding:utf-8
 
'''示例8: 装饰器带类参数'''
 
class locker:
    def __init__(self):
        print("locker.__init__() should be not called.")
 
    @staticmethod
    def acquire():
        print("locker.acquire() called.(这是静态方法)")
 
    @staticmethod
    def release():
        print("  locker.release() called.(不需要对象实例)")
 
def deco(cls):
    '''cls 必须实现acquire和release静态方法'''
    def _deco(func):
        def __deco():
            print("before %s called [%s]." % (func.__name__, cls))
            cls.acquire()
            try:
                return func()
            finally:
                cls.release()
        return __deco
    return _deco
 
@deco(locker)
def myfunc():
    print(" myfunc() called.")
 
myfunc()
myfunc()
"""
before myfunc called [__main__.locker].
locker.acquire() called.(这是静态方法)
 myfunc() called.
  locker.release() called.(不需要对象实例)
before myfunc called [__main__.locker].
locker.acquire() called.(这是静态方法)
 myfunc() called.
  locker.release() called.(不需要对象实例)
"""

9. 第九步:装饰器带类参数,并分拆公共类到其他py文件中,同时演示了对一个函数应用多个装饰器

  • mylocker.py
#!/usr/bin/env python
#coding:utf-8
'''mylocker.py: 公共类 for 示例9.py'''
 
 
class mylocker:
    def __init__(self):
        print("mylocker.__init__() called.")
 
    @staticmethod
    def acquire():
        print("mylocker.acquire() called.")
 
    @staticmethod
    def unlock():
        print("  mylocker.unlock() called.")
 
 
class lockerex(mylocker):
    @staticmethod
    def acquire():
        print("lockerex.acquire() called.")
 
    @staticmethod
    def unlock():
        print("  lockerex.unlock() called.")
 
 
def lockhelper(cls):
    '''cls 必须实现acquire和release静态方法'''
 
    def _deco(func):
        def __deco(*args, **kwargs):
            print("before %s called." % func.__name__)
            cls.acquire()
            try:
                return func(*args, **kwargs)
            finally:
                cls.unlock()
 
        return __deco
 
    return _deco
  • test.py
#!/usr/bin/env python
#coding:utf-8
 
'''示例9: 装饰器带类参数,并分拆公共类到其他py文件中
同时演示了对一个函数应用多个装饰器'''

 
from mylocker import *
 
 
class example:
    @lockhelper(mylocker)
    def myfunc(self):
        print(" myfunc() called.")
 
    @lockhelper(mylocker)
    @lockhelper(lockerex)
    def myfunc2(self, a, b):
        print(" myfunc2() called.")
        return a + b
 
 
if __name__ == "__main__":
    a = example()
    a.myfunc()
    print(a.myfunc())
    print(a.myfunc2(12))
    print(a.myfunc2(34))

11. 元类

11.1 类也是对象

# _*_coding:utf-8_*_
class Person(object):
    print ('person test')
    def __init__(self):
        self.name='hello'

# 当创建了这个类以后,直接运行,就会输出
# person test
# 说明了 类就是对象
  • 通过上面的代码
  • 将在内存中创建一个对象,名字就是Person。这个对象(类对象Person)拥有创建对象(实例对象)的能力。
  • 但是,它的本质仍然是一个对象,于是乎你可以对它做如下的操作:
  • 你可以将它赋值给一个变量
  • 你可以拷贝它
  • 你可以为它增加属性
  • 你可以将它作为函数参数进行传递

11.2 在方法中动态创建类

  • 类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。
  • 首先,你可以在函数中创建类,使用class关键字即可。
# _*_coding:utf-8_*_
def create_class(name):
    if name == 'foo':
        class Foo(object):
            pass
        return Foo # 返回类
    else:
        class Bar(object):
            pass
        return Bar

MyClass = create_class('foo')
print(MyClass) # <class '__main__.Foo'>
print(MyClass()) # <__main__.Foo object at 0x0000000001CF3390>

11.3 使用type创建类

  • type()可以看见对象的类型,但是还可以动态创建类。
  • type可以接受一个类的描述作为参数,然后返回一个类。(要知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是一件很傻的事情,但这在Python中是为了保持向后兼容性)
  • 格式:type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

使用type来创建无属性的类

# _*_coding:utf-8_*_

# MyClass要创建的类名
# () 元祖,要继承的父类
# {} 包含属性的字典(名称和值))
MyClass = type("MyClass",(),{})
c = MyClass()
print(type(c)) # <class '__main__.MyClass'>

使用type创建有属性的类

  • 属性key-value都是加双引号,数字可以不加
# _*_coding:utf-8_*_

MyClass = type('MyClass', (), {"num":0})
c = MyClass()
print(type(c)) # <class '__main__.MyClass'>

使用type创建一个带有普通方法的类

  • 方法的value不加双引号,代表是引用
  • 添加静态方法或动态方法,直接在外面创建好静态方法或动态方法,然后直接添加即可。
# _*_coding:utf-8_*_

def printNum(self):
    print("num:%d"%self.num)
MyClass = type('MyClass', (), {"num":10"printNum":printNum})
c = MyClass()
print(type(c)) # <class '__main__.MyClass'>
c.printNum() # num:10

11.4 __metaclass__属性

  • 你可以在定义一个类的时候为其添加__metaclass__属性。
class Foo(object):
    __metaclass__ = something…
    ...省略...
  • 如果你这么做了,Python就会用元类来创建类Foo。
  • 小心点,这里面有些技巧。你首先写下class Foo(object),但是类Foo还没有在内存中创建。
  • Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类。
  • 把下面这段话反复读几次。当你写如下代码时 :
class Foo(Bar):
    pass
  • Python做了如下的操作:
  • Foo中有__metaclass__这个属性吗?如果是,Python会通过__metaclass__创建一个名字为Foo的类(对象)
  • 如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。
  • 如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。
  • 如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。
  • 这里的__metaclass__中到底写什么呢。
  • 可以以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type都可以。

11.5 自定义元类

  • 元类的主要目的就是为了当创建类时能够自动地改变类。
  • 假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。
  • 有好几种方法可以办到,但其中一种就是通过在模块级别设定__metaclass__
  • 采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。
  • 幸运的是,__metaclass__实际上可以被任意调用,它并不需要是一个正式的类

python2中

#-*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):

    # class_name 会保存类的名字 Foo
    # class_parents 会保存类的父类 object
    # class_attr 会以字典的方式保存所有的类属性

    # 遍历属性字典,把不是__开头的属性名字变为大写
    new_attr = {}
    for name, value in class_attr.items():
        if not name.startswith("__"):
            new_attr[name.upper()] = value

    # 调用type来创建一个类
    return type(class_name, class_parents, new_attr)

class Foo(object):
    __metaclass__ = upper_attr # 设置Foo类的元类为upper_attr
    bar = 'bip'

print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))

f = Foo()
print(f.BAR)

python3中

#-*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):

    #遍历属性字典,把不是__开头的属性名字变为大写
    new_attr = {}
    for name,value in class_attr.items():
        if not name.startswith("__"):
            new_attr[name.upper()] = value

    #调用type来创建一个类
    return type(class_name, class_parents, new_attr)

class Foo(object, metaclass=upper_attr):
    bar = 'bip'

print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))

f = Foo()
print(f.BAR)

用一个真正的class来当做元类

  • 拦截类的创建
  • 修改类
  • 返回修改之后的类
#coding=utf-8

class UpperAttrMetaClass(type):
    # __new__ 是在__init__之前被调用的特殊方法
    # __new__是用来创建对象并返回之的方法
    # 而__init__只是用来将传入的参数初始化给对象
    # 你很少用到__new__,除非你希望能够控制对象的创建
    # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
    # 如果你希望的话,你也可以在__init__中做些事情
    # 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
    def __new__(cls, class_name, class_parents, class_attr):
        # 遍历属性字典,把不是__开头的属性名字变为大写
        new_attr = {}
        for name, value in class_attr.items():
            if not name.startswith("__"):
                new_attr[name.upper()] = value

        # 方法1:通过'type'来做类对象的创建
        return type(class_name, class_parents, new_attr)

        # 方法2:复用type.__new__方法
        # 这就是基本的OOP编程,没什么魔法
        # return type.__new__(cls, class_name, class_parents, new_attr)
# python3的用法
class Foo(object, metaclass=UpperAttrMetaClass):
    bar = 'bip'

# python2的用法
# class Foo(object):
#     __metaclass__ = UpperAttrMetaClass
#     bar = 'bip'


print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出:True

f = Foo()
print(f.BAR)
# 输出:'bip'


原文始发于微信公众号(Python之家):Python基础-28-提高

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/198451.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!