反射特殊双下方法单例模式

Posted shawnhuang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反射特殊双下方法单例模式相关的知识,希望对你有一定的参考价值。

反射

最最重要的,通过字符串去操作对象(类,空间)的属性和方法,字符串形式!!!

# 让用户输入 role 就直接打印 "海绵宝宝"

class A:

    role = "海绵宝宝"

    def func(self):
        print(666)

content = input("请输入:").strip()
ret = getattr(A, content)
print(ret)

反射是一组:四个方法
重要的必须会的
hasattr
getattr
下面的基本不用
setattr
delattr

实例化对象,类,其他py文件, 本py文件

 

# 实例化对象的反射操作

class A:

    country = "中国"
    area = "深圳"

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

    def func(self):
        print(666)

a1 = A("蓬蓬", 18)
print(a1.name)  # 蓬蓬

# 注意这个变量名也要用字符串形式!!
print(hasattr(a1, "name"))  # True

# 不是这样用
print(hasattr(a1, "蓬蓬"))  # False

if hasattr(a1, "name"):
    print(getattr(a1, "name"))
# 蓬蓬

print(getattr(a1, "sex", None))  # None
# 如果没写None就会报错

print(a1.country)  # 中国
print(getattr(a1, "country"))  # 中国

ret = getattr(a1, "func")
print(ret)
# <bound method A.func of <__main__.A object at 0x000001E2D6508A58>>
ret()  # 666

setattr(a1, "sex", "")
print(a1.sex)  #

delattr(a1, "name")
print(a1.name)
# AttributeError: ‘A‘ object has no attribute ‘name‘

 

# 操作类的角度

class A:

    country = "中国"
    area = "深圳"

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

    def func(self):
        print(666)

print(getattr(A, "country"))  # 中国

print(getattr(A, "area"))  # 深圳

# print(getattr(A, "name"))  # 报错

getattr(A, "func")(23)  # 666
# 注意这里不用print
# print(hasattr(A, "func1"))

 

# 操作其他py文件

# 假设有另外一个py文件,内容如下:
flag = True

def func(a):
    print(a)
    return a + 3

class B:

    name_list = ["alex", "abc", "hxy"]

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

    def func(self):
        print(666)


# 如果不用反射,应该在本py文件这样写
import attr

print(attr.flag)

ret = attr.func
print(ret(10))

print(attr.B.name_list)

obj = attr.B("barry", "")
print(obj.name_list)


# 用反射的方法
import attr
print(getattr(attr, "flag"))  # 10

# 1. 找到func并执行
getattr(attr, "func")(10)  # 10
print(getattr(attr, "func")(10))

# 2. 找到B类并调用 name_list func函数
print(getattr(attr, "B"))  # <class ‘attr.B‘>
print(getattr(attr, "B").name_list)  # [‘alex‘, ‘abc‘, ‘hxy‘]
getattr(attr.B, "name_list")  # [‘alex‘, ‘abc‘, ‘hxy‘]
# getattr(attr, "B").func(111)  # 666
getattr(attr.B, "func")(1)  # 666

# 3. 找到B类实例化对象再找 name, name_list, func函数
b1 = getattr(attr, "B")("蓬蓬", "")
print(b1.name)  # 蓬蓬
print(b1.name_list)  # [‘alex‘, ‘abc‘, ‘hxy‘]
b1.func()  # 666

 

# 操作本py文件
# 反射的主体是本文件

# 用户输入func就执行func
import sys

def func():
    print(666)

ret = input("请输入: ").strip()
obj = sys.modules[__name__]
getattr(obj, ret)()  # 666



# 在本文件调用所有的函数

def func1():
    print("in func1")

def func2():
    print("in func2")

def func3():
    print("in func3")

l1 = [func1, func2, func3]
for i in l1:
    i()
# 要是有100个就不能这样了


import sys

l1 = ["func%s" % i for i in range(1, 4)]
print(l1)  # [‘func1‘, ‘func2‘, ‘func3‘]
obj = sys.modules[__name__]
for i in l1:
    getattr(obj, i)()
# in func1
# in func2
# in func3

 

# 到底什么对象才可以用反射?
# 实例化对象,类,其他模块,本模块
# 只有以上四个才能使用,因为他们都能通过 . 的方式获取或调用,也算是一种前提

class A:

    country = "中国"
    area = "深圳"

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

    def func(self):
        print(666)

print(A.country)
print(getattr(A, "country"))
# 因为 上面的都是属于类,因为可以直接用 getattr


# 但是下面只有函数, 不能直接使用 getattr
def func():
    print(111)

def func1():
    print(222)

# 这个时候,应该这样:
import sys
obj = sys.modules[__name__]
getattr(obj, "func")()
getattr(obj, "func1")()

 

# isinstance——判断对象与类的关系

class A:
    pass

class B(A):
    pass

b1 = B()
# 判断 b1 是不是 B 的实例化对象
print(isinstance(b1, B))  # True
# 判断 b1 是不是 A的子类 的实例化对象
print(isinstance(b1, A))  # True

s1 = "asd"
print(isinstance(s1, str))  # True



# issubclass——判断类与类之间的关系
# 判断B类是不是A类的子类,派生类等

print(issubclass(B, A))  # True

 

# 特殊双下方法
# 如果是做项目,一般不要用,除非那些必要的__init__之类的
# 因为这些双下方法一般是给Python开发者在源码中使用

class A:

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.sex = ""

    def __len__(self):
        return len(self.__dict__)

a1 = A("蓬蓬", 18)
# 这里假如要计算实例化对象中 a1 的属性个性
# 我们知道,直接使用len(a1)是不行的,因为类没有 len 方法
# 这时候,可以在A里面添加一个 __len__ 方法,伪装它有 len()
print(len(a1))  # 3

# 通过这个例子可以得知,那些能使用 len()的数据类型内部肯定有 __len__方法

# hash
print(hash(a1)) # -9223371957293561519
# 这里的结果是实例化对象(object) 中有 __hash__ ,而不是A
# 因为A里并没有添加一个 __hash__ 方法

 

# __str__

class A:

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.sex = ""

    def __str__(self):
        print(555)
        return "abc"

a1 = A("蓬蓬", 18)
print(a1)  # 对一个对象打印时,自动执行 __str__ 方法
# 555
# abc



# __call__

class Foo:

    def __init__(self):
        pass

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

obj = Foo()  # 执行 __init__
obj()  # __call__
# 自动触发并执行 __call__

 

# __new__ 构造方法

class A:

    def __init__(self, name):
        self.name = name
        print("in A __init__")

a = A("alex")
# 实例化一个对象时,发生了三件事
# 1. 在内存中开辟了一个对象空间,注意是obj(即a1)中 __new__ 开辟的
# 2. 自动执行 __init__ 方法,将空间传给self
# 3. 在__init__给对象封装属性,并返回给对象

# 也就是说,实例化一个对象的时候,首先执行了 __new__
# 然后执行了 __init__


class A:

    def __init__(self, name):
        self.name = name
        print("in A __init__")

    def __new__(cls, *args, **kwargs):
        print(111)

a1 = A("alex")
print(a1.name)
# 注意本来实例化一个对象 a1 后,a1的 __new__ 方法会在内存中创建一个空间
# 但是,这里类中有 __new__,此时只会执行 A 中的 __new__,即会打印 111
# 也就是说,内存中并没有 a1 的空间,因此也就没有 a1.name
# 最终,print(a1.name) 会报错


# 为了执行obj(a1)的 __new__,可以这样:

class A:

    def __init__(self, name):
        self.name = name
        print("in A __init__")

    def __new__(cls, *args, **kwargs):
        print("in A __new__")
        return object.__new__(cls)

a1 = A("alex")
print(a1.name)
# in A __new__
# in A __init__
# alex
# 根据上面所写实例化对象时的三个步骤,可以推导出以上打印结果

 

# 单例模式——一个类只能实例化一个对象(很重要)

# 一般情况下,一个类可以有很多的实例化对象

class A:
    pass

ret1 = A()
ret2 = A()
ret3 = A()

print(ret1)
print(ret2)
print(ret3)

# <__main__.A object at 0x000001B519D499E8>
# <__main__.A object at 0x000001B519D49A58>
# <__main__.A object at 0x000001B519D49A90>

# 内存地址不一样


class A:

    __instance = None

    def __new__(cls, *args, **kwargs):
        # cls 表示 A, 这一句表示如果是第一次实例化对象
        if cls.__instance is None:
            obj = object.__new__(cls)
            cls.__instance = obj
        return cls.__instance

ret1 = A()
ret2 = A()
ret3 = A()

print(ret1)
print(ret2)
print(ret3)

# <__main__.A object at 0x00000238262B9A58>
# <__main__.A object at 0x00000238262B9A58>
# <__main__.A object at 0x00000238262B9A58>

# 可以看出是在同一个内存地址,也就是说单例模式可以节省内存
# 比如有时很多地方要调用这个类,又必须调用实例对象,就用单例模式

# 通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问
# 从而方便对实例个数的控制并节约系统资源。
# 如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。


class A:

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

    __instance = None

    def __new__(cls, *args, **kwargs):
        # cls 表示 A, 这一句表示如果是第一次实例化对象
        if cls.__instance is None:
            obj = object.__new__(cls)
            cls.__instance = obj
        return cls.__instance

ret1 = A("大家", 20)
ret2 = A("蓬蓬", 28)
ret3 = A("仔仔", 34)

print(ret1)
print(ret2)
print(ret3)
# 内存地址都被覆盖掉了
# <__main__.A object at 0x0000019727D39518>
# <__main__.A object at 0x0000019727D39518>
# <__main__.A object at 0x0000019727D39518>

# 注意这里的结果
print(ret1.name)  # 仔仔
print(ret2.name)  # 仔仔
print(ret3.name)  # 仔仔

 

# item系列

class Foo:

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

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        # 这里不能使用 self.key = value
        # 因为那样相当于 self."age" = 18
        self.__dict__[key]=value

    def __delitem__(self, key):
        print(del obj[key]时,我执行)
        self.__dict__.pop(key)

    def __delattr__(self, item):
        print(del obj.key时,我执行)
        self.__dict__.pop(item)

f1=Foo(sb)
f1[age]=18
f1[age1]=19
del f1.age1  # del obj.key时,我执行
del f1[age]  # del obj[key]时,我执行
f1[name]=alex
print(f1.__dict__)  # {‘name‘: ‘alex‘}

 


















以上是关于反射特殊双下方法单例模式的主要内容,如果未能解决你的问题,请参考以下文章

反射-异常处理-单例模式

枚举实现单例避免被反射破坏的原因

详解单例模式

详解单例模式

单例模式如何防止反射攻击

单例模式--反射--防止序列化破坏单例模式