Python面向对象魔法方法和单例模块代码实例

 更新时间:2020年03月25日 10:19:43   作者:jueyuanfengsheng  
这篇文章主要介绍了Python面向对象魔法方法和单例模块代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

魔法方法

​ 凡是在类内部定义,以“__开头__结尾”的方法都称之为魔法方法,又称“类的内置方法”, 这些方法会在某些条件成立时触发。

经常用到的双下方法

  • __init__: 在调用类时触发。
  • __delarttr__:
  • __getattr__: 会在对象.属性时,“属性没有”的情况下才会触发。对象.__dict__[属性]不会触发__getattr__,会报keyerror;
  • __getattribute__:会在对象.属性时触发,不管有没有该属性都会触发;
  • __setattr__: 会在 “对象.属性 = 属性值” 时触发。即:设置(添加/修改)属性会触发它的执行;
  • __del__: 当对象在内存中被释放时,自动触发执行,该方法会在最后执行。
class Uderline_func:

  x = 100
  
  def __init__(self, y):
    print('类加括号调用的时候触发我!')
    self.y = y # 当与__setattr__方法同时存在时,self.y = y并不会被加载到对象的名称空间
    # self['y'] = y # TypeError: 'Uderline_func' object does not support item assignment
  def general_func(self):
  
    print('随便定义的一个函数!')


  # def __getattr__(self, item):
  #   print('只有对象获取一个没有的属性值得时候触发我!')
  
  def __getattribute__(self, item):
  
    print('类或对象无论获取的属性有没有都会触发我!且出现我,对象点一个没有的属性会覆盖掉__getattr__,还会导致__setattr__函数报错')
  
  def __setattr__(self, key, value):
  
    print('设置属性的时候触发我!')
    # self.a = '在对象名称空间增加一个值!' # 会一直触发__setattr__,出现递归调用
    self.__dict__['a'] = '在对象名称空间增加一个值!'
  def __delattr__(self, item):
  
    print('删除值得时候触发我!')
  
  def __del__(self):
  
    print('程序运行完,被Python解释器回收时,触发我!')
# print(Uderline_func.__dict__) # 类在定义阶段就已经创建好了类名称空间,将其内部变量名和函数名塞进去
u = Uderline_func(100) # 触发__init__
# print(u.__dict__) # {'y': 100}
# Uderline_func.z # 只会触发__getattribute__
u.z # 获取没有的属性触发__getattr__
# u.name = 'zhang' # 触发__setattr__
# del u.x # 对象不能删除掉类中的属性,但只要执行删除操作,都会触发__delattr__的执行
  • __str__: 会在打印对象时触发。
  • __call__: 会在对象被调用时触发。
  • __new__: 会在__init__执行前触发。
class Uderline_func():
  x = 100
  # def __new__(cls, *args, **kwargs):
  #
  #   print('在__init__执行之前触发我,造一个空对象!')
  def __init__(self):
    print('类加括号调用的时候触发我!')
  def __call__(self, *args, **kwargs):
    print('对象加括号调用的时候触发我!')
  def __str__(self):
    print('对象被打印的时候触发我!')
    return '必须要写return返回一个字符串!不然报错"TypeError: __str__ returned non-string (type NoneType)"'
u = Uderline_func()
u()
print(u)

__setitem__,__getitem,__delitem__

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

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

  def __setitem__(self, key, value):
    self.__dict__[key]=value

    # self.age = 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
# print(f1.__dict__)
f1['age1']=19
# del f1.age1
# del f1['age']
f1['name']='alex'
f1.xxx = 111
print(f1.__dict__) # {'name': 'alex', 'age': 18, 'age1': 19, 'xxx': 111}

1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)

2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)

3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代

实例的__dict__

当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。

关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。

class Foo:
  __slots__ = 'x'
f1 = Foo()
f1.x = 1
f1.y = 2 # 报错
print(f1.__slots__) # f1不再有__dict__属性
print(f1.x) #依然能访问
class Bar:
  __slots__ = ['x', 'y']
n = Bar()
n.x, n.y = 1, 2
n.z = 3 # 报错

__doc__:查看类中注释

class Foo:
  '我是描述信息'
  pass
print(Foo.__doc__)
class Foo:
  '我是描述信息'
  pass

class Bar(Foo):
  pass
print(Bar.__doc__) #该属性无法继承给子类

__module__和__class__

__module__:表示当前操作的对象在那个模块

 __class__:表示当前操作的对象的类是什么

class C:
  def __init__(self):
    self.name = ‘SB'
from lib.aa import C
obj = C()
print obj.__module__ # 输出 lib.aa,即:输出模块
print obj.__class__   # 输出 lib.aa.C,即:输出类

__enter__和__exit__

我们知道在操作文件对象的时候可以这么写

with open('a.txt') as f:
  '代码块'

上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

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

  def __enter__(self):
    print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    # return self
  def __exit__(self, exc_type, exc_val, exc_tb):
    print('with中代码块执行完毕时执行我啊')

with Open('a.txt') as f:
  print('=====>执行代码块')
  # print(f,f.name)
  
'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
'''

exit()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

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

  def __enter__(self):
    print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

  def __exit__(self, exc_type, exc_val, exc_tb):
    print('with中代码块执行完毕时执行我啊')
    print(exc_type)
    print(exc_val)
    print(exc_tb)


with Open('a.txt') as f:
  print('=====>执行代码块')
  raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->不会执行


'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
<class 'AttributeError'>
***着火啦,救火啊***
<traceback object at 0x000000000A001E88>
Traceback (most recent call last):
 File "G:/Python代码日常/第一阶段/1阶段/面向对象/test.py", line 52, in <module>
  raise AttributeError('***着火啦,救火啊***')
AttributeError: ***着火啦,救火啊***

'''

如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

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

  def __enter__(self):
    print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

  def __exit__(self, exc_type, exc_val, exc_tb):
    print('with中代码块执行完毕时执行我啊')
    print(exc_type)
    print(exc_val)
    print(exc_tb)
    return True
  with Open('a.txt') as f:
  print('=====>执行代码块')
  raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->会执行
class Open:
  def __init__(self,filepath,mode='r',encoding='utf-8'):
    self.filepath=filepath
    self.mode=mode
    self.encoding=encoding

  def __enter__(self):
    # print('enter')
    self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
    return self.f

  def __exit__(self, exc_type, exc_val, exc_tb):
    # print('exit')
    self.f.close()
    return True 
  def __getattr__(self, item):
    return getattr(self.f,item)

with Open('a.txt','w') as f:
  print(f)
  f.write('aaaaaa')
  f.wasdf #抛出异常,交给__exit__处理

用途或者说好处:

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

单例模式

单例模式:多次实例化的结果指向同一个实例

方式1

# @classmethod(用类绑定方法)

import settings

class MySQL:
  __instance=None
  def __init__(self, ip, port):
    self.ip = ip
    self.port = port
  @classmethod
  def from_conf(cls):
    if cls.__instance is None:
      cls.__instance=cls(settings.IP, settings.PORT)
    return cls.__instance
obj1=MySQL.from_conf()
obj2=MySQL.from_conf()
obj3=MySQL.from_conf()
# obj4=MySQL('1.1.1.3',3302)
print(obj1)
print(obj2)
print(obj3)
# print(obj4)

方式2

# 用类装饰器
import settings

def singleton(cls):
 _instance=cls(settings.IP,settings.PORT)
 def wrapper(*args,**kwargs):
   if len(args) !=0 or len(kwargs) !=0:
     obj=cls(*args,**kwargs)
     return obj
   return _instance
 return wrapper

@singleton #MySQL=singleton(MySQL) #MySQL=wrapper
class MySQL:
 def __init__(self, ip, port):
   self.ip = ip
   self.port = port

# obj=MySQL('1.1.1.1',3306) #obj=wrapper('1.1.1.1',3306)
# print(obj.__dict__)

obj1=MySQL() #wrapper()
obj2=MySQL() #wrapper()
obj3=MySQL() #wrapper()
obj4=MySQL('1.1.1.3',3302) #wrapper('1.1.1.3',3302)
print(obj1)
print(obj2)
print(obj3)
print(obj4)

方式3

# 调用元类
import settings

class Mymeta(type):
  def __init__(self,class_name,class_bases,class_dic):
    #self=MySQL这个类
    self.__instance=self(settings.IP,settings.PORT)

  def __call__(self, *args, **kwargs):
    # self=MySQL这个类
    if len(args) != 0 or len(kwargs) != 0:
      obj=self.__new__(self)
      self.__init__(obj,*args, **kwargs)
      return obj
    else:
      return self.__instance

class MySQL(metaclass=Mymeta): #MySQL=Mymeta(...)
  def __init__(self, ip, port):
    self.ip = ip
    self.port = port
obj1=MySQL()
obj2=MySQL()
obj3=MySQL()
obj4=MySQL('1.1.1.3',3302)
print(obj1)
print(obj2)
print(obj3)
print(obj4)

方式4

# 利用模块多次导入只产生一次名称空间,多次导入只沿用第一次导入成果。
def f1():
  from singleton import instance
  print(instance)

def f2():
  from singleton import instance,My
  SQL
  print(instance)
  obj=MySQL('1.1.1.3',3302)
  print(obj)

f1()
f2()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 修改默认的pip版本为对应python2.7的方法

    修改默认的pip版本为对应python2.7的方法

    今天小编就为大家分享一篇修改默认的pip版本为对应python2.7的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-11-11
  • Python Selenium如何切换浏览器的页面

    Python Selenium如何切换浏览器的页面

    这篇文章主要介绍了Python Selenium如何切换浏览器的页面的相关资料,在使用Selenium进行网页测试时,跳转页面后常会出现无法定位元素的问题,解决这一问题的关键是学会在多个浏览器标签页或窗口间切换,需要的朋友可以参考下
    2024-10-10
  • 一文实现删除numpy数组中的指定索引元素

    一文实现删除numpy数组中的指定索引元素

    在Python中,Numpy是一个强大的数学库,用于处理大型多维数组和矩阵的数学运算,数组是由相同类型的数据元素组成的集合,并且每个元素都可以通过索引进行访问,本文将给大家介绍如何删除numpy数组中的指定索引元素,需要的朋友可以参考下
    2024-05-05
  • 50行Python代码实现人脸检测功能

    50行Python代码实现人脸检测功能

    现在的人脸识别技术已经得到了非常广泛的应用,支付领域、身份验证、美颜相机里都有它的应用。下面小编给大家带来了基于50行Python代码实现人脸检测功能,一起看看吧
    2018-01-01
  • pytorch 中forward 的用法与解释说明

    pytorch 中forward 的用法与解释说明

    这篇文章主要介绍了pytorch 中forward 的用法与解释说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • tornado+celery的简单使用详解

    tornado+celery的简单使用详解

    今天小编就为大家分享一篇tornado+celery的简单使用详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • Python 利用pandas和mysql-connector获取Excel数据写入到MySQL数据库

    Python 利用pandas和mysql-connector获取Excel数据写入到MySQL数据库

    在实际应用中,我们可能需要将Excel表格中的数据导入到MySQL数据库中,以便于进行进一步的数据分析和处理,本文将介绍如何使用Python将Excel表格中的数据插入到MySQL数据库中,需要的朋友可以参考下
    2023-10-10
  • Python numpy中np.random.seed()的详细用法实例

    Python numpy中np.random.seed()的详细用法实例

    在学习人工智能时,大量的使用了np.random.seed(),利用随机数种子,使得每次生成的随机数相同,下面这篇文章主要给大家介绍了关于Python numpy中np.random.seed()的详细用法,需要的朋友可以参考下
    2022-08-08
  • Python操作Mysql实例代码教程在线版(查询手册)

    Python操作Mysql实例代码教程在线版(查询手册)

    本文介绍了Python操作MYSQL、执行SQL语句、获取结果集、遍历结果集、取得某个字段、获取表字段名、将图片插入数据库、执行事务等各种代码实例和详细介绍,代码居多,是一桌丰盛唯美的代码大餐
    2013-02-02
  • Django实现简单登录的示例代码

    Django实现简单登录的示例代码

    本文主要介绍了Django实现简单登录的示例代码,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11

最新评论