Python中的装饰器和描述符

Posted by JustDoDT on January 17, 2018

1. 单例模式

类每次实例化的时候都会创建一个新的对象,如果要求类只能被实例化一次该怎么做呢?

用__new__实现单例模式

class Earth:
    def __new__(cls):
        if not hasattr(cls,'instance'):
            cls.instance = super().__new__(cls)
        return cls.instance
    def __init__(self):
        self.name = 'earth'
e = Earth()
print(e,id(e))
a = Earth()
print(a,id(a))

执行上面的代码的运行结果:

<__main__.Earth object at 0x00000200888EB4A8> 2201314309288
<__main__.Earth object at 0x00000200888EB4A8> 2201314309288

在上面的例子中,可以看到两个实例的ID是相同的,即这2个实例的ID是相同的,意味着这2个其实是引用的同一个实例,是一个实例的不同名字。

class Earth(object):  #实例本身,cls类本身
    def __new__(cls, *args, **kwargs):  # new开辟了一个内存空间
        if not hasattr(cls,'instance'):
            cls.instance = super().__new__(cls)
        return cls.instance

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

# new 方法是在类实例化的时候最先被执行
# 开辟新的内存空间出来
# 重写new 方法
# 单例模式

b = Earth('哈哈')  # new 单例模式
c = Earth('呵呵')  # 覆盖
print(b.name)
print(c.name)

# 用 new实现单例模式,当成一个范式,是固定的写法;单例模式可以节省内存;继承了单例的类,子类也会变成单例模式

class A(Earth):
    pass
a = A()
d = A()
print(a,d)

2. 定制属性访问

2.1 查询

在python中所有的东西都是类

hasattr(re, 'length')	# 返回bool值
getattr(re,  'length')	# 返回属性值
b. __getattribute__('length') # 返回属性值

class Rectangle:
    def __init__(self,width,length):
        self.width = width
        self.length = length
    def getArea(self):
        area = self.length * self.width
        return '面积为:%s' % area

a = Rectangle(4,5)
print(hasattr(a,'length'))  # 判断有没有这个属性,返回为bool类型
print(getattr(a,'width'))  # 返回属性值
print(a.__getattribute__('length'))

执行上面代码的运行结果为:

True
4
5

2.2 修改

#__setattr__
class Rectangle:
    def __init__(self,width,length):
        self.width = width
        self.length = length
    def getArea(self):
        area = self.length * self.width
        return '面积为:%s' % area

a = Rectangle(4,5)
print(hasattr(a,'length'))  # 判断有没有这个属性,返回为bool类型
print(getattr(a,'width'))  # 返回属性值
setattr(a,'length',6)      # 把长修改为6
print(a.__getattribute__('length'))

执行上面代码的结果为:

True
4
6

2.3 添加

# 有则改,无则增
# __setattr__

class Rectangle:
    def __init__(self,width,length):
        self.width = width
        self.length = length
    def getArea(self):
        area = self.length * self.width
        return '面积为:%s' % area

a = Rectangle(4,5)
a.aaa = 'hahaha'
setattr(a,'bb',20)
print(getattr(a,'aaa'))
print(getattr(a,'bb'))

执行上面代码运行的结果为:

hahaha
20

2.4 删除

#__delattr__
class Rectangle:
    def __init__(self,width,length):
        self.width = width
        self.length = length
    def getArea(self):
        area = self.length * self.width
        return '面积为:%s' % area

a = Rectangle(4,5)
print(getattr(a,'length'))  # 返回属性值
delattr(a,'length')
print(hasattr(a,'length'))

执行上面代码运行的结果为:

5
False  # 表明已经删除了'length'属性

3. 描述符

如果在一个类中实例化另一个类,对这个属性进行访问的时候怎么做的? 用描述符

描述符可以按照你自己定制的你想要怎么去访问这个类,也就是按照你重写代码的逻辑去执行

# 这类里面实例化另一个类,对这个类做访问时,需要定义__get__ , __set__ , __delete__ 方法
class MyClass:
    def __get__(self, instance, owner):
        print('恭喜您,获得了S级武器')
    def __set__(self, instance, value):
        print('This is %s' % value)
    def __delete__(self, instance):
        print('武器已经损坏')

class Control:
    attr = MyClass()  # 实例化

c = Control()
c.attr        # 调用属性,会去MyClass类里面执行__get__方法
c.attr = 20   # 重新赋值
del(c.attr)

执行上面的代码运行的结果为:

恭喜您,获得了S级武器
This is 20
武器已经损坏

4. 装饰器

4.1 闭包

def outer():
    a = 2
    def inner():
        print(a)
    return inner   # 返回出来的是一个函数体
f = outer()    # inner
f()            # inner()

执行以上代码的结果为:

2



def outer(func):   # 参数,函数体
    def inner():
        print('-------------')
        func()
    return inner   # 返回出来的是一个函数体
def func():
    print('----正在登陆------')
outer(func)
a = outer(func)    # 返回值,对象   a = inner
a()            # inner()

执行上面的代码运行的结果为:

-------------
----正在登陆------

4.2 装饰器

装饰器他可以在不改变原有函数的基础上给函数增加一些新的功能,他也是一种语法糖,简化代码的,类似于三目运算和lambda表达式。

def outer(func):   # 参数,函数体
    def inner():
        print('-------------')
        func()
    return inner   # 返回出来的是一个函数体
@outer   # f1 = outer(f1)     #f1 = outer(f1)
def f1():      # 登陆功能
    print('登陆功能')
f1()        # inner()
@outer
def f2():
    print('退出功能')
f2()

执行上面的代码运行的结果为:

-------------
登陆功能
-------------
退出功能

4.3 内置装饰器

class Rectangle(object):
    def __init__(self,width,length):
        self.width = width
        self.length = length
    @property
    def getArea(self):
        return self.length * self.width
    @staticmethod   # 静态方法,不能访问类里面其他的类属性跟类方法
    def func():
        print('我没有写self')
    @classmethod     # 类方法,可以访问类属性跟类方法
    def func2(cls):
        print('哈哈哈哈哈哈')
Rectangle.func2()    #解绑

执行上面代码运行的结果为:

哈哈哈哈哈哈

class TestClass:
    def __init__(self,func):
        self.func = func
    def __call__(self, *args, **kwargs):
        print('--------call---------')
        return self.func()    #注意:类当装饰器一定要用__call__
@TestClass
def aaa():
    print('hahahhah')
aaa()

执行以上代码的结果为:

--------call---------
hahahhah

4.4 测试程序执行时间的装饰器

# 定义一个测试程序执行时间的装饰器
from datetime import datetime
def run_time(func):
    def new_func(*args,**kwargs):
        start_time = datetime.now()
        print('程序开始时间:%s'%start_time)
        func(*args,**kwargs)
        end_time = datetime.now()
        print('程序结束时间:%S'%end_time)
        print('程序总共执行的时间:%s'%(end_time - start_time))
    return new_func()

5. 习题

测试type和isinstance两个函数,哪个速度更加的快。

from datetime import datetime
def run_time(func):
    def new_func(*args,**kwargs):
        start_time = datetime.now()
        # print('程序开始时间:%s'%start_time)
        func(*args,**kwargs)
        end_time = datetime.now()
        # print('程序结束时间:%s'%end_time)
        print('程序总共执行的时间:%s'%(end_time - start_time))
    return new_func

@run_time
def test_typetime():
    print('##########测试type执行的时间###########')
    for i in range(1000000):
        type('hahahaha')

@run_time
def test_isinstancetime():
    print('##########测试isinstance执行的时间###########')
    for i in range(1000000):
        isinstance('hahahaha',str)

test_typetime()
test_isinstancetime()

执行上面代码运行的结果为:

##########测试type执行的时间###########
程序总共执行的时间:0:00:00.073759
##########测试isinstance执行的时间###########
程序总共执行的时间:0:00:00.090758

代码及结果截图:

avatar