python与设计模式


python与设计模式


基本概念

每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。 这样你就能一次又一次地使用该方案而不必做重复劳动

三种最基本的设计模式:

创建模式,提供实例化的方法,为适合的状况提供相应的对象创建方法。

结构化模式,通常用来处理实体之间的关系,使得这些实体能够更好地协同工作。

行为模式,用于在不同的实体建进行通信,为实体之间的通信提供更容易,更灵活的通信方法

设计模式六大原则

开闭原则:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。

里氏(Liskov)替换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象。

依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程。

接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。

迪米特法则:一个软件实体应当尽可能少地与其他实体发生相互作用。

单一职责原则:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。 

接口

接口: 一种特殊的类,声明了若干方法,要求继承该接口的类必须实现这些方法。 作用: 限制继承接口的类的方法的名称及调用方式;隐藏了类的内部实现。

接口就是一种抽象的基类(父类),限制继承它的类必须实现接口中定义的某些方法。

Python中使用ABCMetaabstractmethod 的抽象类、抽象方法来实现接口的功能。

接口类定义方法,不具体实现,限制子类必须有该方法。在接口子类中实现具体的功能。

python 中实现接口类的方式

(1)
from abc import ABCMeta
from abc import abstractmethod  # 导入抽象方法

class Father(metaclass=ABCMeta):  # 创建抽象类
    
    @abstractmethod
    def f1(self):
        pass

    @abstractmethod
    def f2(self):
        pass

class F1(Father):
    def f1(self):
        pass

    def f2(self):
        pass

    def f3(self):
        pass

obj = F1()



创建型模式


简单工厂模式

内容:

不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。

角色:

工厂角色(Creator)
抽象产品角色(Product)
具体产品角色(Concrete Product)

优点:

隐藏了对象创建的实现细节
客户端不需要修改代码

缺点:

违反了单一职责原则,将创建逻辑集中到一个工厂类里
当添加新产品时,需要修改工厂类代码,违反了开闭原则

    from abc import abstractmethod, ABCMeta
    
    
    class Payment(metaclass=ABCMeta):
        @abstractmethod
        def pay(self, money):
            pass
    
    
    class Alipay(Payment):
        def __init__(self, enable_yuebao=False):
            self.enable_yuebao = enable_yuebao
    
        def pay(self, money):
            if self.enable_yuebao:
                print("余额宝支付%s元" % money)
            else:
                print("支付宝支付%s元" % money)
    
    
    class ApplePay(Payment):
        def pay(self, money):
            print("苹果支付%s元" % money)
    
    
    class PaymentFactory:
        def create_payment(self, method):
            if method == "alipay":
                return Alipay()
            elif method == 'yuebao':
                return Alipay(enable_yuebao=True)
            elif method == "applepay":
                return ApplePay()
            else:
                raise NameError(method)
    
    
    f = PaymentFactory()
    p = f.create_payment("yuebao")
    p.pay(100)



工厂方法模式(Factory Method)

内容:

定义一个用于创建对象的接口(工厂接口),让子类决定实例化哪一个产品类。

角色:

抽象工厂角色(Creator)
具体工厂角色(Concrete Creator)
抽象产品角色(Product)
具体产品角色(Concrete Product)

工厂方法模式相比简单工厂模式将每个具体产品都对应了一个具体工厂。

适用场景:

需要生产多种、大量复杂对象的时候。
需要降低耦合度的时候。
当系统中的产品种类需要经常扩展的时候。

优点:

每个具体产品都对应一个具体工厂类,不需要修改工厂类代码
隐藏了对象创建的实现细节

缺点:

每增加一个具体产品类,就必须增加一个相应的具体工厂类

    from abc import abstractmethod, ABCMeta
    
    
    class Payment(metaclass=ABCMeta):
        @abstractmethod
        def pay(self, money):
            pass
    
    
    class Alipay(Payment):
        def pay(self, money):
            print("支付宝支付%s元" % money)
    
    
    class ApplePay(Payment):
        def pay(self, money):
            print("苹果支付%s元" % money)
    
    
    class PaymentFactory(metaclass=ABCMeta):
        @abstractmethod
        def create_payment(self):
            pass
    
    
    class AlipayFactory(PaymentFactory):
        def create_payment(self):
            return Alipay()
    
    
    class ApplePayFactory(PaymentFactory):
        def create_payment(self):
            return ApplePay()
    
    
    af = AlipayFactory()
    ali = af.create_payment()
    ali.pay(120)



抽象工厂方法

内容:

定义一个工厂类接口,让工厂子类来创建一系列相关或相互依赖的对象。

例:生产一部手机,需要手机壳、CPU、操作系统三类对象进行组装,其中每类对象都有不同的种类。对每个具体工厂,分别生产一部手机所需要的三个对象。

角色:

抽象工厂角色(Creator)
具体工厂角色(Concrete Creator)
抽象产品角色(Product)
具体产品角色(Concrete Product)
客户端(Client)

相比工厂方法模式,抽象工厂模式中的每个具体工厂都生产一套产品。

适用场景:

系统要独立于产品的创建与组合时
强调一系列相关的产品对象的设计以便进行联合使用时
提供一个产品类库,想隐藏产品的具体实现时

优点:

将客户端与类的具体实现相分离
每个工厂创建了一个完整的产品系列,使得易于交换产品系列
有利于产品的一致性(即产品之间的约束关系)

缺点:

难以支持新种类的(抽象)产品

    from abc import abstractmethod, ABCMeta
    
    
    # ------抽象产品------
    class PhoneShell(metaclass=ABCMeta):
        @abstractmethod
        def show_shell(self):
            pass
    
    
    class CPU(metaclass=ABCMeta):
        @abstractmethod
        def show_cpu(self):
            pass
    
    
    class OS(metaclass=ABCMeta):
        @abstractmethod
        def show_os(self):
            pass
    
    
    # ------抽象工厂------
    
    class PhoneFactory(metaclass=ABCMeta):
        @abstractmethod
        def make_shell(self):
            pass
    
        @abstractmethod
        def make_cpu(self):
            pass
    
        @abstractmethod
        def make_os(self):
            pass
    
    
    # ------具体产品------
    
    class SmallShell(PhoneShell):
        def show_shell(self):
            print("普通手机小手机壳")
    
    
    class BigShell(PhoneShell):
        def show_shell(self):
            print("普通手机大手机壳")
    
    
    class AppleShell(PhoneShell):
        def show_shell(self):
            print("苹果手机壳")
    
    
    class SnapDragonCPU(CPU):
        def show_cpu(self):
            print("骁龙CPU")
    
    
    class MediaTekCPU(CPU):
        def show_cpu(self):
            print("联发科CPU")
    
    
    class AppleCPU(CPU):
        def show_cpu(self):
            print("苹果CPU")
    
    
    class Android(OS):
        def show_os(self):
            print("Android系统")
    
    
    class IOS(OS):
        def show_os(self):
            print("iOS系统")
    
    
    # ------具体工厂------
    
    class MiFactory(PhoneFactory):
        def make_cpu(self):
            return SnapDragonCPU()
    
        def make_os(self):
            return Android()
    
        def make_shell(self):
            return BigShell()
    
    
    class HuaweiFactory(PhoneFactory):
        def make_cpu(self):
            return MediaTekCPU()
    
        def make_os(self):
            return Android()
    
        def make_shell(self):
            return SmallShell()
    
    
    class IPhoneFactory(PhoneFactory):
        def make_cpu(self):
            return AppleCPU()
    
        def make_os(self):
            return IOS()
    
        def make_shell(self):
            return AppleShell()
    
    
    # ------客户端------
    
    class Phone:
        def __init__(self, cpu, os, shell):
            self.cpu = cpu
            self.os = os
            self.shell = shell
    
        def show_info(self):
            print("手机信息:")
            self.cpu.show_cpu()
            self.os.show_os()
            self.shell.show_shell()
    
    
    def make_phone(factory):
        cpu = factory.make_cpu()
        os = factory.make_os()
        shell = factory.make_shell()
        return Phone(cpu, os, shell)
    
    
    p1 = make_phone(HuaweiFactory())
    p1.show_info()



建造者模式(Builder)

内容:

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

角色:

抽象建造者(Builder)
具体建造者(Concrete Builder)
指挥者(Director)
产品(Product)

建造者模式与抽象工厂模式相似,也用来创建复杂对象。主要区别是建造者模式着重一步步构造一个复杂对象,而抽象工厂模式着重于多个系列的产品对象。

适用场景:

当创建复杂对象的算法(Director)应该独立于该对象的组成部分以及它们的装配方式(Builder)时
当构造过程允许被构造的对象有不同的表示时(不同Builder)。

优点:

隐藏了一个产品的内部结构和装配过程
将构造代码与表示代码分开
可以对构造过程进行更精细的控制

    from abc import abstractmethod, ABCMeta
    
    # ------产品------
    
    class Player:
        def __init__(self, face=None, body=None, arm=None, leg=None):
            self.face = face
            self.arm = arm
            self.leg = leg
            self.body = body
    
        def __str__(self):
            return "%s, %s, %s, %s" % (self.face, self.arm, self.body, self.leg)
    
    
    # ------建造者------
    
    class PlayerBuilder(metaclass=ABCMeta):
        @abstractmethod
        def build_face(self):
            pass
    
        @abstractmethod
        def build_arm(self):
            pass
    
        @abstractmethod
        def build_leg(self):
            pass
    
        @abstractmethod
        def build_body(self):
            pass
    
        @abstractmethod
        def get_player(self):
            pass
    
    
    class BeautifulWomanBuilder(PlayerBuilder):
        def __init__(self):
            self.player = Player()
    
        def build_face(self):
            self.player.face = "漂亮脸蛋"
    
        def build_arm(self):
            self.player.arm = "细胳膊"
    
        def build_body(self):
            self.player.body = "细腰"
    
        def build_leg(self):
            self.player.leg = "长腿"
    
        def get_player(self):
            return self.player
    
    
    class PlayerDirector:
        def build_player(self, builder):
            builder.build_body()
            builder.build_arm()
            builder.build_leg()
            builder.build_face()
            return builder.get_player()
    
    
    director = PlayerDirector()
    builder = BeautifulWomanBuilder()
    p = director.build_player(builder)
    print(p)



单例模式(Singleton)

内容:

保证一个类只有一个实例,并提供一个访问它的全局访问点。

角色:

单例(Singleton)

适用场景

当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时

优点:

对唯一实例的受控访问
单例相当于全局变量,但防止了命名空间被污染。
与单例模式功能相似的概念:全局变量、静态变量(方法)

class Singleton(object):
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            cls._instance = super(Singleton, cls).__new__(cls, )
        return cls._instance


class MyClass(Singleton):
    a = 1

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


one = MyClass('name1')
two = MyClass('name2')

print(id(one))
print(id(two))
print(one == two)
print(one is two)



def singleton(cls, *args, **kw):
    instances = {}

    def get_instance():
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]

    return get_instance


@singleton
class MyClass2:
    a = 1


one = MyClass2()
two = MyClass2()

print(id(one))  # 31495472
print(id(two))  # 31495472
print(one == two)
print(one is two)



原型模式(Prototype)

内容:

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

使用场景:

通过动态装载;
为了避免创建一个与产品类层次平行的工厂类层次时;
当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

import copy


class Prototype:
    def __init__(self):
        self._objects = {}

    def register_object(self, name, obj):
        """Register an object"""
        self._objects[name] = obj

    def unregister_object(self, name):
        """Unregister an object"""
        del self._objects[name]

    def clone(self, name, **attr):
        """Clone a registered object and update inner attributes dictionary"""
        obj = copy.deepcopy(self._objects.get(name))
        obj.__dict__.update(attr)
        return obj


def main():
    class A:
        def __str__(self):
            return "I am A"

    a = A()
    prototype = Prototype()
    prototype.register_object('a', a)
    b = prototype.clone('a', a=1, b=2, c=3)

    print(a)
    print(b.a, b.b, b.c)


if __name__ == '__main__':
    main()

创建型模式总结

使用抽象工厂(Abstract Factory)、原型(Prototype)或者建造者(Builder)的设计甚至比工厂方法(Factory Method)的那些设计更灵活,

依赖于继承的创建型模式:工厂方法模式

依赖于组合的创建型模式:抽象工厂模式、创建者模式


结构性模式


适配器模式 (Adapter Class/Object)

内容:

将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

角色:

目标接口(Target)
待适配的类(Adaptee)
适配器(Adapter)

两种实现方式:

类适配器:使用多继承
对象适配器:使用组合

适用场景:

想使用一个已经存在的类,而它的接口不符合你的要求
(对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配它们的接口。
对象适配器可以适配它的父类接口。

类适配器:

用一个具体的Adapter类对Adaptee和Target进行匹配。结果是当我们想要匹配一个类以及所有他的子类时,类Adaptee将不能胜任工作。
使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类。
仅仅引入一个对象,并不需要额外的指针以间接得到Adaptee。

对象适配器:

允许一个Adapter与多个Adaptee——即Adaptee本身以及它所有的子类(如果有子类的话)一同时工作。Adapter也可以一次给所有的Adaptee添加功能。
使得重定义Adaptee的行为比较困难。这酒需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。
 
 
 from abc import abstractmethod, ABCMeta
 
 
 class Payment(metaclass=ABCMeta):
     @abstractmethod
     def pay(self, money):
         raise NotImplementedError
 
 
 class Alipay(Payment):
     def pay(self, money):
         print("支付宝支付%s元" % money)
 
 
 class ApplePay(Payment):
     def pay(self, money):
         print("苹果支付%s元" % money)
 
 
 # ------待适配类------
 
 class WechatPay:
     def cost(self, money):
         print("微信支付%s元" % money)
 
 
 # 类适配器
 class RealWechatPay(WechatPay, Payment):
     def pay(self, money):
         return self.cost(money)
 
 
 # 对象适配器
 
 class RealWechatPay2(Payment):
     def __init__(self):
         self.payment = WechatPay()
 
     def pay(self, money):
         return self.payment.cost(money)
 
 
 p = RealWechatPay2()
 p.pay(111)
 
 


组合模式(Composite)

内容:

将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

角色:

抽象组件(Component)
叶子组件(Leaf)
复合组件(Composite)
客户端(Client)

适用场景:
    
    表示对象的“部分-整体”层次结构(特别是结构是递归的)
    希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中的所有对象

优点:

定义了包含基本对象和组合对象的类层次结构
简化客户端代码,即客户端可以一致地使用组合对象和单个对象
更容易增加新类型的组件

缺点:

很难限制组合中的组件

from abc import abstractmethod, ABCMeta

class Graphic(metaclass=ABCMeta):
    @abstractmethod
    def draw(self):
        pass

    @abstractmethod
    def add(self, graphic):
        pass

    def getchildren(self):
        pass

# 图元

class Point(Graphic):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def draw(self):
        print(self)

    def add(self, graphic):
        raise TypeError

    def getchildren(self):
        raise TypeError

    def __str__(self):
        return "点(%s, %s)" % (self.x, self.y)


class Line(Graphic):
    def __init__(self, p1, p2):
        self.p1 = p1
        self.p2 = p2

    def draw(self):
        print(self)

    def add(self, graphic):
        raise TypeError

    def getchildren(self):
        raise TypeError

    def __str__(self):
        return "线段[%s, %s]" % (self.p1, self.p2)


class Picture(Graphic):
    def __init__(self):
        self.children = []

    def add(self, graphic):
        self.children.append(graphic)

    def getchildren(self):
        return self.children

    def draw(self):
        print("------复合图形------")
        for g in self.children:
            g.draw()
        print("------END------")


pic1 = Picture()
point = Point(2,3)
pic1.add(point)
pic1.add(Line(Point(1,2), Point(4,5)))
pic1.add(Line(Point(0,1), Point(2,1)))

pic2 = Picture()
pic2.add(Point(-2,-1))
pic2.add(Line(Point(0,0), Point(1,1)))

pic = Picture()
pic.add(pic1)
pic.add(pic2)

pic.draw()
#pic1.draw()
#point.draw()



代理模式 (Proxy)

内容:

为其他对象提供一种代理以控制对这个对象的访问。

角色:

抽象实体(Subject)
实体(RealSubject)
代理(Proxy)

适用场景:

    远程代理:为远程的对象提供代理
    虚代理:根据需要创建很大的对象
    保护代理:控制对原始对象的访问,用于对象有不同访问权限时 优点:

远程代理:可以隐藏对象位于远程地址空间的事实
虚代理:可以进行优化,例如根据要求创建对象
保护代理:允许在访问一个对象时有一些附加的内务处理

from abc import ABCMeta, abstractmethod


class Subject(metaclass=ABCMeta):
    @abstractmethod
    def get_content(self):
        pass

    def set_content(self, content):
        pass


class RealSubject(Subject):
    def __init__(self, filename):
        self.filename = filename
        print("读取%s文件内容" % filename)
        f = open(filename)
        self.__content = f.read()
        f.close()

    def get_content(self):
        return self.__content

    def set_content(self, content):
        f = open(self.filename, 'w')
        f.write(content)
        self.__content = content
        f.close()


# ---远程代理

class ProxyA(Subject):
    def __init__(self, filename):
        self.subj = RealSubject(filename)

    def get_content(self):
        return self.subj.get_content()

    def set_content(self, content):
        return self.subj.set_content(content)


# ---虚代理

class ProxyB(Subject):
    def __init__(self, filename):
        self.filename = filename
        self.subj = None

    def get_content(self):
        if not self.subj:
            self.subj = RealSubject(self.filename)
        return self.subj.get_content()


x = ProxyB('abc.txt')
# print(x.get_content())

# ---保护代理

class ProxyC(Subject):
    def __init__(self, filename):
        self.subj = RealSubject(filename)

    def get_content(self):
        self.subj.get_content()

    def set_content(self, content):
        raise PermissionError

filename = "abc.txt"
username = input()
if username!="demo":
    p = ProxyC(filename)
else:
    p = ProxyA(filename)

print(p.get_content())


行为模式


责任链模式(Chain of Responsibility)

内容:

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

角色:

抽象处理者(Handler)
具体处理者(ConcreteHandler)
客户端(Client)

例:

    请假部门批准:leader—>部门经理—>总经理
    Javascript事件浮升机制

适用场景:

    有多个对象可以处理一个请求,哪个对象处理由运行时决定
    在不明确接收者的情况下,向多个对象中的一个提交一个请求

优点:

降低耦合度:一个对象无需知道是其他哪一个对象处理其请求

缺点:

请求不保证被接收:链的末端没有处理或链配置错误

from abc import ABCMeta, abstractmethod


class Handler(metaclass=ABCMeta):
    @abstractmethod
    def handle_leave(self, day):
        pass


class GeneralManagerHandler(Handler):
    def handle_leave(self, day):
        if day < 10:
            print("总经理批准%d天假" % day)
            return True
        else:
            print("呵呵")
            return False


class DepartmentManagerHandler(Handler):
    def __init__(self):
        self.successor = GeneralManagerHandler()

    def handle_leave(self, day):
        if day < 7:
            print("部门经理批准%d天假" % day)
            return True
        else:
            print("部门经理无权准假")
            return self.successor.handle_leave(day)


class ProjectDirectorHandler(Handler):
    def __init__(self):
        self.successor = DepartmentManagerHandler()

    def handle_leave(self, day):
        if day < 3:
            print("项目主管批准%d天假" % day)
            return True
        else:
            print("项目主管无权准假")
            return self.successor.handle_leave(day)


day = 11
h = ProjectDirectorHandler()
print(h.handle_leave(day))



迭代器模式(Iterator)

内容:

提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

适用场景:

访问一个聚合对象的内容而无需暴露它的内部表示。
支持对聚合对象的多种遍历。
为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)

实现方法:__iter____next__


class LinkList:
    """链表 头结点保存链表的长度"""

    class Node:
        def __init__(self, item=None):
            self.item = item
            self.next = None

    class LinkListIterator:
        def __init__(self, node):
            self.node = node

        def __next__(self):
            if self.node:
                cur_node = self.node
                self.node = cur_node.next
                return cur_node.item
            else:
                raise StopIteration

        def __iter__(self):
            return self

    def __init__(self, iterable=None):
        self.head = LinkList.Node(0)
        self.tail = self.head
        self.extend(iterable)

    def append(self, obj):
        s = LinkList.Node(obj)
        self.tail.next = s
        self.tail = s
        self.head.item += 1

    def extend(self, iterable):
        for obj in iterable:
            self.append(obj)

    def __iter__(self):
        return self.LinkListIterator(self.head.next)

    def __len__(self):
        return self.head.item

    def __str__(self):
        return "<<" + ", ".join(map(str, self)) + ">>"


li = [i for i in range(100)]
lk = LinkList(li)
print(lk)



观察者模式(Observer)

内容:

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。观察者模式又称“发布-订阅”模式

角色:

抽象主题(Subject)
具体主题(ConcreteSubject)——发布者
抽象观察者(Observer)
具体观察者(ConcreteObserver)——订阅者

适用场景:

当一个抽象模型有两方面,其中一个方面依赖于另一个方面。将这两者封装在独立对象中以使它们可以各自独立地改变和复用。
当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。

优点:

目标和观察者之间的抽象耦合最小
支持广播通信

缺点:

多个观察者之间互不知道对方存在,因此一个观察者对主题的修改可能造成错误的更新。

from abc import ABCMeta, abstractmethod


class Observer(metaclass=ABCMeta):
    @abstractmethod
    def update(self, notice):
        pass


class Notice:
    def __init__(self):
        self.observers = []

    def attach(self, obs):
        self.observers.append(obs)

    def detach(self, obs):
        self.observers.remove(obs)
        # obs.company_info=None

    def notify(self):
        for obj in self.observers:
            obj.update(self)


class ManagerNotice(Notice):
    def __init__(self, company_info=None):
        super().__init__()
        self.__company_info = company_info

    def detach(self, obs):
        super().detach(obs)
        obs.company_info = None

    @property
    def company_info(self):
        return self.__company_info

    @company_info.setter
    def company_info(self, info):
        self.__company_info = info
        self.notify()


class Manager(Observer):
    def __init__(self):
        self.company_info = None

    def update(self, noti):
        self.company_info = noti.company_info


notice = ManagerNotice()

a = Manager()
b = Manager()

print(alex.company_info)
print(wusir.company_info)

notice.attach(a)
notice.attach(b)
notice.company_info = "info1"

print(a.company_info)
print(b.company_info)

notice.company_info = "info2"

print(a.company_info)
print(b.company_info)

notice.detach(wusir)

notice.company_info = "info3"

print(a.company_info)
print(b.company_info)

notice.company_info = "info4"

print(a.company_info)
print(b.company_info)


Buy me a 肥仔水!