社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python魔法方法 & 单例模式

程序媛小庄 • 4 年前 • 281 次点击  
阅读 11

Python魔法方法 & 单例模式

这是我参与更文挑战的第14天,活动详情查看: 更文挑战

微信公众号搜索【程序媛小庄】,关注半路出家的程序媛如何靠python开发养家糊口~

前言

本文介绍一下python中常用的魔法方法以及面向对象中非常重要的单例模式。

魔法方法

python中一切皆对象,因为python是面向对象的编程语言。python给类和对象提供了大量的内置方法,这些内置方法也称魔法方法。这些魔法方法总是在某种条件下自动触发执行,就像魔法一样。

__init__方法

该方法是用来接收定义类时类中__new__方法返回的空对象后为空对象进行初始化的操作,没有返回值。

class Test():
    def __init__(self, name):
        self.name = name
        
    def test(self):
        print(self.name)
   
t = Test('xu')
t1 = Test('python')
复制代码

__new__方法

该方法是当类被调用实例化对象时首先被触发的方法,用来实例化一个空对象并返回。

class Test():
    def __new__(cls,*args, **kwargs):
        return object.__new__(cls, *args, **kwargs) 
    
    def __init__(self, name):
        self.name = name
复制代码

__call__方法

如果想让一个对象变成一个可调用对象(加括号可以调用),需要在该对象的类中定义__call__方法,调用可调用对象的返回值就是__call__方法的返回值。

class Test():
    
    def __init__(self):
        self.name = 'python'
    
    def __call__(self, *args, **kwargs):  # self是Test类的对象
        print(self)  # <__main__.Test object at 0x000001C78CE78FD0>
        print(self.name)
        
t = Test()
t()  # python
复制代码

__str___方法

当对象被访问打印时触发执行,该方法必须有一个字符串类型的返回值。

class Test():
    def __init__(self, name):
        self.name = name
	
    def __str__(self):
        return self.name
   
t = Test('xu')
print(t1)  # xu
复制代码

__del___方法

__del__方法是在对象被删除时自动触发,由于python的垃圾回收机制会自动清理程序中没用的资源,因此如果一个对象只是占用应用程序的资源,没有必要定义__del__方法,但是如果设计到占用系统资源的话比如打开的文件对象,由于关系到操作系统的资源,python的垃圾回收机制派不上用场的时候,就需要为对象创建__del__方法,用于对象被删除后自动触发回收操作系统资源。

class Test:
    def __init__(self):
        self.x = open('a.txt',mode='w')
        # self.x = 占用的是操作系统资源

    def __del__(self):
        print('run')
        # 发起系统调用,告诉操作系统回收相关的系统资源
        self.x.close()

obj = T()
del obj # obj.__del__() 
复制代码

__enter__ & __exit__方法

使用with上下文管理时,会触发对象中的__enter__方法,并将__enter__方法的返回值赋值给as声明的变量。

with语句正常结束的时候会触发__exit__方法,该方法的三个参数分别代表异常类型、异常值和溯源信息,如果with语句代码块出现异常,则with语句后的代码都不会被执行,但是如果该方法返回值为True,异常会被清空,with代码块后的代码还会被正常执行。代码如下:

class Open:
    def __init__(self):
        self.name = 'open'

    def __enter__(self):
        print('with语句执行时会首先执行的方法,返回值会赋值给as声明的变量')
        return self.name

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中的代码块执行完毕时执行exit')
        print(exc_type, '如果出现异常表示异常类型')
        print(exc_val, '表示异常的值')
        print(exc_tb, '表示异常的溯源信息')
        return 123  # 非零 非空 非None为真

    
with Open() as test:
    print(test)
    raise TypeError('看一下错误信息')
print(


    
'我会不会被执行呢')  # 当__exit__方法返回值为真时,会被执行,否则不会被执行
复制代码

item系列方法

item系列方法包括__setitem__、__getitem__、delitem__方法,这三种方法分别会在中括号赋值/修改值、中括号取值、中括号删除值时触发,比如可以自定义一个字典类,并自定义中括号赋值、取值、删除值的方法:

class MyDict(dict):

    def __setitem__(self, key, value):
        print('执行setitem', key, value)  # 执行setitem, x, 1
        self.__dict__[key] = value

    def __getitem__(self, item):
        print('执行getitem', item)  # 执行getitem x
        print(self.__dict__[item])  # 1

    def __delitem__(self, key):
        print('执行delitem', key)  # 执行delitem x
        self.__dict__.pop(key)


d = MyDict()
d['x'] = 1
print(d['x'])
del d['x']
复制代码

attr系列方法

attr系列方法包括__setattr__,__getattr__,__delattr____setattr__在添加/修改属性时会触发,___delattr__删除属性的时候触发,__getattr__在使用.调用属性并且属性不存在时触发。如下代码所示

class Test:
    def __init__(self):
        self.name = 'python'

    def __setattr__(self, key, value):
        print('添加/修改属性setattr')
        self.__dict__[key] = value
        # self.key = value  # 会出现无线递归,因为对象.属性会调用__setattr__方法

    def __delattr__(self, item):
        print('删除属性delattr')
        self.__dict__.pop(item)

    def __getattr__(self, item):
        print('属性不存在时调用getattr')


t = Test()
t.x = 'x'
print(t.y)
del t.x
复制代码

单例模式

单例模式是一种软件设计模式,为了保证一个类无论调用多少次产生的对象都指向同一个内存地址,即仅仅只有一个对象。

实现单例模式的方式有很多,总的原则就是保证一个类只要实例化一个对象,因此关键点就是如何判断这个类是否实例化过一个对象。

这里介绍几种实现方式,供大家参考:

模块导入的方式

这种方式的原理是模块导入后只运行一次,后面再次使用该模块中的类是直接从内存中查找。

# cls_singleton.py
class Foo(object):
    pass

instance = Foo()

# test.py
import cls_singleton

obj1 = cls_singleton.instance
obj2 = cls_singleton.instance
print(obj1 is obj2)  # True
复制代码

通过__new__方法

原理就是判断类是否有实力,有就直接返回,没有就保存到_instance

class Test:

    _instance = None

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

    def __new__(cls, *args, **kwargs):
        # if cls._instance:
        #     return cls._instance	                # 有实例则直接返回
        # else:
        #     cls._instance = super().__new__(cls)	# 没有实例则new一个并保存
        #     return cls._instance	                # 这个返回是给是给init,再实例化一次,也没有关系

        if not cls._instance:	                        # 这是简化的写法,上面注释的写法更容易提现判断思路
            cls._instance = super().__new__(cls)
        return cls._instance


t1 = Test('python', 18)
t2 = Test('python1', 18)
print(t1 is t2)  # True
复制代码

自定义元类的方式

这种方式的原理是类调用的过程,类定义时会调用元类下的__init__,类调用(实例化对象)时会触发元类下的__call__方法。

class Mymeta(type):

    def __init__(cls, name, bases, dic):
        super().__init__(name, bases, dic)
        cls._instance = None		                  # 将记录类的实例对象的数据属性放在元类中自动定义了

    def __call__(cls, *args, **kwargs):	                  # 此call会在类被调用(即实例化时触发)
        if cls._instance:				  # 判断类有没有实例化对象
            return cls._instance
        else:						  # 没有实例化对象时,控制类造空对象并初始化
            obj = cls.__new__(cls, *args, **kwargs)
            obj.__init__(*args, **kwargs)
            cls._instance = obj			          # 保存对象,下一次再实例化可以直接返回而不用再造对象
            return obj


class Test(metaclass=Mymeta):
    def __init__(self, name, age):
        self.name = name
        self.age = age


t1 = Test('python', 18)
t2 = Test('python1', 18)
print(t1 is t2)  # True
复制代码

结语

文章首发于微信公众号程序媛小庄,同步于掘金知乎

码字不易,转载请说明出处,走过路过的小伙伴们伸出可爱的小指头点个赞再走吧(╹▽╹)

wallhaven-z8ykry.jpg

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/115595
 
281 次点击