Python设计模式

Python创建型设计模式

原型模式

根据现有对象复制出新对象并对其进行修改。

class Point:
    __slot__ = ('x','y')
    def __init__(self, x, y):
        self.x = x
        self.y = y
使用原型模式创建对象
point1 = Point(1,2)
point2 = copy.deepcopy(ponit1)
point2.x = 3
point2.y = 4
point3 = point1.__class__(6, 7)

代码中的point2和point3都是通过“原型模式”创建的新对象,其中point2是通过先复制出一个新的对象,然后再重新初始化该对象,point3是通过__class__传入新参数直接创建新对象,相对更加简洁。

单例模式

在整个程序运行过程中,如果某个类只有一个实例,那么可通过单例模式来保证。
Python实现单例的方法是:创建模块时,把全局状态放在私有变量中,并提供一个用于访问此变量的公开函数。

Python结构型设计模式

主要用途:是将一种对象改装为另一种对象,或将小对象拼合成大对象。

修饰器(decorator)

一般是单参数的函数,其参数也是函数,修饰器所返回的新函数与经由函数传入的原函数名称相同但功能更强,框架经常通过修饰器把用户所编写的函数集成进来。此外,还有类修饰器,其参数为一个类。

函数修饰器与方法修饰器

所有的函数修饰器与方法修饰器的结构大体相同
首先,创建包装函数(一般命名为wrapper()),然后在包装函数中调用原函数。最后,修饰器把包装函数作为调用结果返回,返回后的函数会以原函数的名义取代。

包装函数的返回值也很灵活:可以把原函数的调用结果直接返回,也可以先修改后返回,又或者可以返回其他值。

修饰器以"@"开头,后面是修饰器的名字。

@float_args_and_return
def mean(first, second, *rest):
    numbers = (first, second) + rest
    return sum(numbers)/len(numbers)
    
def float_args_and_return(function):
    @functools.wrap(function)
    def wrapper(*args, **kwargs):
        args = [float(arg) for arg in args]
        return float(function(*args, **kwargs))
    return wrapper

float_args_and_return()是函数修饰器,所以接受一个函数作为其参数。将*args和**kwargs作为包装函数的参数实际上等于令包装函数接受任意的参数
包装函数应该把传入的参数全都传给原函数。创建好包装函数后,修饰器会将其返回。

为防止经过修饰后的函数的__name__属性发生改变,可以使用@functools.wraps修饰器,在修饰器里面用其来修饰包装函数。(最好总是使用)

为什么要使用修饰器?

拿上述代码举例,若是没经过修饰器修饰的函数,mean()函数接受的是两个数值进行相加取平均值的函数,但如果传入的参数是’7.6’和7的话,会出现TypeError,因为str和int型是不能相加的,但若是修饰过后便不会抛出错误,因为参数在修饰器的包装函数中会通过float(‘7.6’)和float(7)经过强制转换成有效的数值。

@statically_typed(str, int, return_type=str)
def repeat(what, count, separator):
    return ((what + separator)*count)[:-len(separator)]

函数statically_typed()是一个修饰器工厂,为了指定受修饰的函数能够接受何种类型的参数,返回那种类型的值,此为statically_typed函数的作用

创建修饰器工厂有着一套固定的流程。首先,创建修饰器函数,在该函数内创建包装函数,在包装函数尾部把调用原函数所得到的返回值返回给上一层。在修饰器尾部返回包装函数。最后,在修饰器工厂函数尾部返回修饰器。

类修饰器

@ensure("title", is_not_empty_str)
@ensure("price", is_in_range(1, 10000))
class Book:
    def __init__(self, title, price):
        self.title = title
        self.price = price
    
    @property
    def value(self):
        return self.price

ensure()函数接受两个参数,一个是属性名字,另一个是“验证器函数”,然后会返回一个类修饰器,这个类修饰器会运用在@ensure之后的类上面

上述函数的运行过程:先创建一个Book的类,然后使用ensure()函数返回的类修饰器来修饰Book,修饰后的类多了名为“price”的属性,以此类推。由此可见,运行步骤是从下至上,与编写顺序相反。

ensure()函数会以给定的属性名称,验证器函数及可选的docstring为参数来创建类修饰器,而调用函数就相当于用它所创建的类修饰器来修饰特定的类,修饰后的类会有新的属性。

也可以有另外一种写法

@do_ensure
class Book:
    title = Ensure(is_not_empty_str)
    price = Ensure(is_in_range(1, 10000))
    def __init__(self, title, price):
        self.title = title
        self.price = price
    
    @property
    def value(self):
        return self.price

上面是改写的类,用了@do_ensure类修饰器和Ensure实例来实现与前例相同的功能。每次构造Ensure对象的时候都要传入验证函数,而@do_Ensure会用带有验证机制的同名属性来替换相应的Ensure实例。

源码:

class Ensure:
    def __init__(self, validate, doc=None):
        self.validate = validate
        self.doc = doc
def do_ensure(Class):
    def make_property(name, attribute):
        privateName = "__" + name
        def getter(self):
            return getattr(self, privateName)
        def setter(self, value):
            attribute.validate(name, value)
            setattr(self, privateName, value)
        return property(getter, setter, doc=attribute.doc)
    for name,attribute in Class.__dict__.items():
        if isinstance(attribute, Ensure):
            setattr(Class, name, make_property(name, attribute))
    return Class

Ensure类用来保存验证器函数。do_ensure可分为三部分,第一部分,定义了名为make_preperty()的“嵌套”函数。该函数有两个参数,一个是属性名,另一个是Ensure类型的attribute,此函数将返回一个属性,该属性会把值保存在私有的attribute。setter函数会调用原来的实例验证器函数。第二部分里,遍历类中的每一个attribute,并用新的属性来替换原来的Ensure实例,第三部分把修改后的类返回。

用类修饰器实现继承
class Mediated:
    def __init__(self):
        self.mediator = None
    
    def on_change(self):
        if self.mediator is not None:
            self.mediator.on_change(self)

上面代码片段提供了一个名为Mediated的基类,子类用通常的方法来继承它(例如class XXX(Mediated))。但因为子类不用修改继承的方法,所以也可以用类修饰器的方法来实现继承。

def mediated(Class):
    setattr(Class, "mediator", None)
    def on_change(self):
        if self.mediator is not None:
            self.mediator.on_change(self)
    setattr(Class, "on_change", on_change)
    return Class

类修饰器的用法为:@mediated class 要继承的子类名:…来使用,修饰后的类的行为与通过继承得来的类相同。

全部评论

相关推荐

头像
11-06 10:58
已编辑
门头沟学院 嵌入式工程师
双非25想找富婆不想打工:哦,这该死的伦敦腔,我敢打赌,你简直是个天才,如果我有offer的话,我一定用offer狠狠的打在你的脸上
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务