python猴子补丁:修改类的__new__

本文探讨了Python中的单例模式实现,通过修改类的__new__方法实现。首先介绍了Python类的访问权限,并展示了标准的单例模式实现。接着,讲解了如何使用types模块的MethodType动态修改类的方法,以实现单例。在过程中,指出了super()在类外部使用时的限制,并提供了正确的使用方式。最后,通过示例展示了动态修改__new__方法对单例模式的影响,以及不同场景下的内存分配和属性变化情况。

python猴子补丁:修改类的__new__

1 python的单例模式(python的类中,_属性是protected变量,__属性是private变量,一般的属性就是public变量,概念上和java类似,但有区别,python的私有变量只是概念上的,可以通过dir(类名)打印获取私有变量,本质上可以通过_类 _ _属性访问):

class A:
    _obj = None

    def __new__(cls, *args, **kwargs):
        if not cls._obj:
            cls._obj = super().__new__(cls)
        return cls._obj

    def __init__(self, x):
        self.x = x
        
a = A(1)
print(id(a), a.x)
b = A(2)
print(id(b), b.x)
#结果如下,a和b实例对象共用一个内存空间,且实例对象的属性不同
17648440 1
17648440 2

2 如果希望动态的为类增加单例模式,也就是需要重写类的__new__方法,就需要动态修改类的实例方法,需要使用tpyes模块,使用types.MethodType,将函数名和实例对象传入,进行方法绑定(python的类绑定实例属性直接:类.属性=xxx即可,python动态语言的美妙)
在这里插入图片描述
查看MethodType的源码可知,传入参数function(callable的对象,python中函数和类都是callable的)和对象即可

import types


class A:
    _obj = None

    def __init__(self, x):
        self.x = x
        
        
def new(cls, *args, **kwargs):
    if not cls._obj:
        cls._obj = super().__new__(cls)
    return cls._obj


A.__new__ = types.MethodType(new, A)
a = A(1)
print(id(a), a.x)
b = A(2)
print(id(b), b.x)
#报错:
RuntimeError: super(): __class__ cell not found

在类外部定义的函数中,不能在没有参数的情况下使用super(), __class__单元格super()所依赖的元素仅提供给class主体中定义的功能。从super() 文档可知:

零参数形式仅在类定义内起作用,因为编译器会填写必要的详细信息以正确检索要定义的类,以及为常规方法访问当前实例。

super使用的方式:

  1. super(类名,对象名).方法():既可在类的内部也可在类的外部使用,如继承了父类的子类的:
    super(子类名,self). _ _ init _ _(父类init的参数)

  2. 父类类名.方法名(self):A.b(self) 既可在内部也可也在外部使用

  3. super().xxx():只能在类的内部,如:super()._ _ init _ _()

修改如下:

import types


class A:
    _obj = None

    # def __new__(cls, *args, **kwargs):
    #     if not cls._obj:
    #         cls._obj = super().__new__(cls)
    #     return cls._obj

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


def new(cls, *args, **kwargs):
    if not cls._obj:
        # cls._obj = super(A, cls).__new__(cls)
        # 将super的第一个参数改为cls,可以作为类名
        cls._obj = super(cls, cls).__new__(cls)
    return cls._obj


A.__new__ = types.MethodType(new, A)
a = A(1)
print(id(a), a.x)
b = A(2)
print(id(b), b.x)
# 结果如下:可见内存空间地址一致,且实例的属性改变了
28396392 1
28396392 2

另一种情况:

import types


class A:
    _obj = None

    # def __new__(cls, *args, **kwargs):
    #     if not cls._obj:
    #         cls._obj = super().__new__(cls)
    #     return cls._obj

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


class B:
    def __init__(self, x):
        self.x = x


def new(cls, *args, **kwargs):
    # cls._obj=None
    if not cls._obj:
        # cls._obj = super(A, cls).__new__(cls)
        # 将super的第一个参数改为cls,可以代替所有的子类
        cls._obj = super(cls, cls).__new__(cls)
    return cls._obj


A.__new__ = types.MethodType(new, A)
B._obj = None
B.__new__ = types.MethodType(new, B)

a = A(1)
print(id(a), a.x)
b = A(2)
print(id(b), b.x)
print(id(a), a.x)

c = B(5)
d = B(8)
print(id(c), c.x)
print(id(d), d.x)
print(id(c), c.x)

# 结果如下:对于A来说,实例共用的一个内存空间,所以实例化b后,a也是指向的
# 同一个内存空间,所以x的值相同,对于B来说,因为外部的全局变量B._obj,
# 是类共用的类属性(不是每个实例对象单独开辟的空间),所以实例对象可以
# 成为单例模式,x均是8

# 因为在python中,类只会开辟1次空间,实例一般情况下会开辟多个空间
28659064 1
28659064 2
28659064 2
28659256 8
28659256 8
28659256 8
# 如果做如下修改
def new(cls, *args, **kwargs):
    cls._obj=None
    if not cls._obj:
        # cls._obj = super(A, cls).__new__(cls)
        # 将super的第一个参数改为cls,可以代替所有的子类
        cls._obj = super(cls, cls).__new__(cls)
    return cls._obj
# 将_obj存入类的__new__方法中,外部的B._obj = None去掉,这种方式  
# 每次生成实例对象,都会生成1个新的对象
# 重新执行
# 结果中,B的两个实例对象的x和内存地址均改变(包括A类,因为A类
# 和B类都是使用的同一个__new__)
26168672 1
26168840 2
26168672 1
26168888 5
26168936 8
Python Directx是一种Python的图形处理库,而猴子补丁和元则是Python语言中的高级特性。 1. 猴子补丁 猴子补丁Python语言中的一种技巧,可以在运行时动态地修改或模块的行为。通过猴子补丁,可以在不改变原有代码的情况下,为现有的或模块添加新的方法或属性,或者修改现有的方法或属性。 例如,可以使用猴子补丁修改Python Directx库中的某个方法的行为,而不需要修改Python Directx库的原始代码。 下面是一个简单的例子,演示了如何使用猴子补丁修改一个的行为: ```python import some_module def monkey_patch(): some_module.SomeClass.some_method = new_method def new_method(self): print('This is the new method') monkey_patch() c = some_module.SomeClass() c.some_method() # This is the new method ``` 2. 元Python语言的高级特性,用于控制的创建过程。通过元,可以在定义时动态地修改的行为,或者在创建的实例时添加新的属性或方法。 例如,可以使用元来自动为Python Directx库中的所有添加某个方法或属性。 下面是一个简单的例子,演示了如何使用元来自动为一个添加一个属性: ```python class MyMetaClass(type): def __new__(cls, name, bases, attrs): attrs['my_attribute'] = 42 return super().__new__(cls, name, bases, attrs) class MyClass(metaclass=MyMetaClass): pass c = MyClass() print(c.my_attribute) # 42 ``` 在上面的例子中,定义了一个元MyMetaClass,它在创建的过程中会自动为该添加一个属性my_attribute,然后创建了一个MyClass,并将其元设置为MyMetaClass。当创建MyClass的实例时,该实例会继承MyClass的属性和方法,同时也会自动继承my_attribute属性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值