python之元类、单例模式

目录

 

一、补充:面向对象的静态方法

二、eval与exec内置函数

三、元类

3.1、什么事元类?

3.2、不依赖class创建一个自定义类

3.3、自定义元类中的一些方法

四、单例模式的实现

4.1、模块导入实现

4.2、约定用类方法来获取

4.3、重写__new__方法实现

4.4、装饰器完成

4.5、自定义元类实现


一、补充:面向对象的静态方法

功能体中永远不需要使用类与对象
既不绑定给类,也不绑定给对象

class A:
    def t1(self):
        pass

    @classmethod
    def t2(cls):
        pass

    @staticmethod
    def t3():
        pass

二、eval与exec内置函数

将字符串作为执行目标,得到相应结果
abc = 10
print(eval(abc))   # eval() arg 1 must be a string, bytes or code object

eval内置函数的使用场景:
    1、执行字符串会得到相应的执行结果,有返回值
    2、一般用于类型转化,得到dict,list,tuple等

dic_str = "{'a': 1, 'b': 2, 'c': 3}"
print(eval(dic_str),type(eval(dic_str)))  # {'a': 1, 'b': 2, 'c': 3} <class 'dict'>
list_str = "[1, 2, 3, 4, 5]"
print(eval(list_str),type(eval(list_str)))  # [1, 2, 3, 4, 5] <class 'list'>
tuple_str = "(1, 2, 3, 4, 5)"
print(eval(tuple_str),type(eval(tuple_str)))  # (1, 2, 3, 4, 5) <class 'tuple'>

exec应用场景
  1.执行字符串没有执行结果(没有返回值)
  2.将执行的字符串中产生的名字形成对应的局部名称空间

source = '''
name = 'Bob'
age = 20
'''

class A:
    pass
a = A()
dic = {}
exec(source, {}, dic)
print(dic)  # {'name': 'Bob', 'age': 20}
a.__dict__ = dic
print(a.__dict__)  # {'name': 'Bob', 'age': 20}
print(a.name)  # Bob
print(a.age)  # 20

三、元类

3.1、什么事元类?

在python中,一切皆对象。而对象都是调用类实例化后得到的。既然一切皆对象,那么类也是对象,用来创建类的类就是元类:type。即类是type类的对象。

class OldboyTeacher:
    school = 'oldboy'
    def __init__(self, name):
        self.name = name

    def score(self):
        print('%s is scoring' % self.name)

tea1 = OldboyTeacher('shj')
print(type(tea1))  # <class '__main__.OldboyTeacher'>
print(type(OldboyTeacher))  # <class 'type'>

tea1是调用OldboyTeacher类得到的,OldboyTeacher类也是一个对象,只要是对象,都是调用一个类实例化得到的,即OldboyTeacher=type(*args,**kwargs)

关系:
调用元类(实例化)-->自定义的类
调用自定义的类-->自定义的对象

3.2、不依赖class创建一个自定义类

一、class关键字创建自定义类的底层原理
1、先拿到类名
2、再拿到类的基类们,默认都是object
3、然后拿到类的名称空间
4、调用元类(type)实例化得到自定义的类:OldboyTeacher = type('Oldboyteacher',(object,),{……})

二、自定义类的三个关键组成:
1、类名
2、类的基类们
3、类的名称空间

案例: 

class_name = 'Oldboy'
class_bases = (object,)
class_dic = {}
class_body = """
school = 'oldboy'
def __init__(self, name):
    self.name = name

def score(self):
    print('%s is scoring' % self.name)
"""
exec(class_body,{},class_dic)
obj = type(class_name, class_bases, class_dic)
# 验证
print(obj)  # <class '__main__.Oldboy'>,成功创建类Oldboy
print(obj.__dict__)

t = obj('shj')  # 可以正常实例化,得到名称空间,用来存放产生的名字
print(t.__dict__)  # {'name': 'shj'}

3.3、自定义元类中的一些方法

# 自定义异常
class CountError(Exception):
    pass

class MyMeta(type):  # 但凡继承了type的类才能成为元类,否则就只是一个普通的类
    # 自定义元类,重写init方法的目的:
    # 1.该方法是从type中继承来的,所以参数同type的init
    # 2.最终的工作(如果开辟空间,如果操作内存)还是要借助type
    # 3.在交给type最终完成工作之前,可以对类的创建加以限制 *****
    def __init__(cls, class_name, bases, namespace):
        print(cls)  # # 继承MyMeta的类
        print(class_name)
        print(bases)
        print(namespace)
        # 需求:给元类控制的类的类名必须首字母大写
        if not class_name.istitle():
            raise NameError('名字首字母必须大写')
        # 需求:定义类时必须明确父类
        if len(bases) == 0:
            raise CountError('父类必须明确')
        # 控制文档注释不能为空
        doc = namespace.get('__doc__')
        if doc is None or len(doc) == 0 or len(doc.strip('\n ')) == 0:
            raise TypeError("文档注释不能为空,且必须存在")

        # super(MyMeta, cls).__init__(class_name, bases, namespace)
        super().__init__(class_name, bases, namespace) # 继承type

    # 自定义元类,重写call方法的目的:
    # 1.被该元类控制的类生成对象,会调用元类的call方法
    # 2.在call中的返回值就是创建的对象
    # 3.在call中
    #       -- 通过object开辟空间产生对象
    #       -- 用被控制的类回调到自己的init方法完成名称空间的赋值
    #       -- 将修饰好的对象反馈给外界
    def __call__(cls, *args, **kwargs):
        print('call fn run')
        # 创造一个空对象
        obj = object.__new__(cls)  # obj是Student这个类的对象
        # 需求:所有通过该元类控制的类产生的对象都有meta_name该属性
        obj.meta_name = cls.__name__
        # obj.name = args[0]
        # 执行__init__方法,完成对象的初始属性操作,进行名称空间的赋值
        cls.__init__(obj, *args, **kwargs)
        return obj


class Student(object, metaclass=MyMeta):  # 如果不声明object,会报错:CountError('父类必须明确')
    '''文档注释不能为空'''

    def __init__(self, name, age):
        print('init fn run')
        self.name = name
        self.age = age

stu = Student("Bob",18)  # 会触发元类的__call__
print(stu)
print(stu.name)
print(stu.age)

# 问题:
# 1.继承是想获得父级的属性和方法,元类是要将类的创建于对象的创建加以控制
# 2.类的创建由元类的__init__方法控制
#		-- 元类(class_name, bases, namespase) => 元类.__init__来完成实例化
# 3.类的对象的创建由元类的__call__方法控制
# 		-- 对象产生是需要开辟空间,在__call__中用object.__new__()来完成的
class Student(object, metaclass=MyMeta):
    '''文档注释不能为空'''
    pass

# class Student:  <=>  type(class_name, bases, namespace)

四、单例模式的实现

单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。

4.1、模块导入实现

Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。

SingleModule文件

class Songs():
    pass

# s1 = Songs()
# s2 = Songs()
# print(s1==s2)  # False,实例化的对象地址不一样

# 对外提供的对象
song = Songs()

 test1文件

def t():
    from 模块导入实现.singleModule import song
    print(song)

test文件

# 如果需要song对象,任何文件在任何地方通过导入拿到的是唯一对象

from 模块导入实现.singleModule import song
print(song)
# <模块导入实现.singleModule.Songs object at 0x00000009EC7A7828>

from 模块导入实现.singleModule import song
print(song)
# <模块导入实现.singleModule.Songs object at 0x00000009EC7A7828>

from 模块导入实现.singleModule import song
print(song)
# <模块导入实现.singleModule.Songs object at 0x00000009EC7A7828>

from 模块导入实现.test1 import t
t()
# <模块导入实现.singleModule.Songs object at 0x00000009EC7A7828>

4.2、约定用类方法来获取

class Songs():
    __instance = None
    @classmethod
    def getInstance(cls):
        # 对象没有就创建并返回,存在时直接返回
        if cls.__instance == None:
            cls.__instance = cls()
        return cls.__instance

# 约定别用 类名() 来实例化对象,用类方法来获取唯一对象
s1 = Songs.getInstance()
s2 = Songs.getInstance()
print(s1==s2)  # True,内存地址一样

4.3、重写__new__方法实现

'''
class Songs:
    # 类一旦重写__new__方法,该类的实例化由__new__来控制
    def __init__(self):
        print('init')

    def __new__(cls, *args, **kwargs):
        print(args)
        print(kwargs)
        print('new')

s1 = Songs(1, 2)
s2 = Songs(3, 4)
print(s1, s2)  # __new__没设返回值,此处为空值
'''

class Songs:
    __instance = None
    def __new__(cls, song_name, *args, **kwargs):
        if cls.__instance == None:
            cls.__instance = object.__new__(cls)
            cls.__instance.song_name = song_name
        return cls.__instance

    def change_song(self, song_name):
        self.song_name = song_name

s1 = Songs("Mylove")
s2 = Songs("倾城")
print(s1==s2)  # True
print(s1.song_name,s2.song_name)  # Mylove Mylove

s2.change_song("倾城")
print(s1.song_name,s2.song_name)  # 倾城 倾城

4.4、装饰器完成

def outer(cls):
    __instance = None
    def inner(*args,**kwargs):
        nonlocal __instance
        if __instance == None:
            __instance = cls(*args,**kwargs)
        return __instance
    return inner

@outer  # Songs = outer(Songs)
class Songs:
    pass

s1 = Songs()
s2 = Songs()
print(s1 is s2)  # True,内存地址相同




还有一种就是不传参时就是同一个对象,如果传了其他参数就另外生成一个对象
settings文件:
IP = '1.1.1.1'
PORT = 3306

单例实现文件:
from settings import IP, PORT

def Singleton(cls):
    __instance = cls(IP, PORT)
    def inner(*args, **kwargs):
        if args or kwargs:
            obj = cls(*args, **kwargs)
            return obj
        return __instance
    return inner

@Singleton
class MySQL:
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port

obj1 = MySQL()
obj2 = MySQL()
obj3 = MySQL()
print(obj1)  # <__main__.MySQL object at 0x000000838F7A6710>
print(obj2)  # <__main__.MySQL object at 0x000000838F7A6710>
print(obj3)  # <__main__.MySQL object at 0x000000838F7A6710>

4.5、自定义元类实现

class SingleMeta(type):
    __instance = None
    def __call__(cls, *args, **kwargs):
        if SingleMeta.__instance == None:
            SingleMeta.__instance = object.__new__(cls)
            cls.__init__(SingleMeta.__instance,*args,**kwargs)
        return SingleMeta.__instance

class Songs(metaclass=SingleMeta):
    def __init__(self):
        pass
s1 = Songs()
s2 = Songs()
print(s1 is s2)  # True,地址一样




# 从配置文件导入配置
settings文件
IP = '1.1.1.1'
PORT = 3306


# 元类实现单例
from settings import IP, PORT
class Mymeta(type):
    # self 就是MySQL这个类
    def __init__(self, class_name, class_bases, class_dic):
        self.__instance = self(IP,PORT)

    def __call__(self, *args, **kwargs):
        if args or kwargs:
            obj = self.__new__(self)
            self.__init__(obj, *args, **kwargs)
            return obj
        else:
            return self.__instance


class MySQL(metaclass=Mymeta):
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port
        
obj1 = MySQL()  # <__main__.MySQL object at 0x0000000C02800B00>
obj2 = MySQL()  # <__main__.MySQL object at 0x0000000C02800B00>
obj3 = MySQL()  # <__main__.MySQL object at 0x0000000C02800B00>
print(obj1)
print(obj2)
print(obj3)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值