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


Posted in Python onMarch 25, 2020

魔法方法

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

经常用到的双下方法

  • __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()

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

Python 相关文章推荐
python实现2014火车票查询代码分享
Jan 10 Python
python搭建微信公众平台
Feb 09 Python
Python向日志输出中添加上下文信息
May 24 Python
python统计多维数组的行数和列数实例
Jun 23 Python
python高效过滤出文件夹下指定文件名结尾的文件实例
Oct 21 Python
基于Python在MacOS上安装robotframework-ride
Dec 28 Python
python使用selenium实现批量文件下载
Mar 11 Python
关于python字符串方法分类详解
Aug 20 Python
Python3 操作 MySQL 插入一条数据并返回主键 id的实例
Mar 02 Python
Pytorch 高效使用GPU的操作
Jun 27 Python
Python爬虫爬取微信朋友圈
Aug 06 Python
Python批量将csv文件转化成xml文件的实例
May 10 Python
Python语法垃圾回收机制原理解析
Mar 25 #Python
python实现Pyecharts实现动态地图(Map、Geo)
Mar 25 #Python
Pyecharts 动态地图 geo()和map()的安装与用法详解
Mar 25 #Python
Django查询优化及ajax编码格式原理解析
Mar 25 #Python
python使用pyecharts库画地图数据可视化的实现
Mar 25 #Python
python实现3D地图可视化
Mar 25 #Python
简单了解django处理跨域请求最佳解决方案
Mar 25 #Python
You might like
php使用指定字符列表生成随机字符串的方法
2015/04/18 PHP
CodeIgniter使用smtp服务发送html邮件的方法
2015/06/10 PHP
php中二分法查找算法实例分析
2016/09/22 PHP
jquery获取input的value问题说明
2010/08/19 Javascript
Javascript事件热键兼容ie|firefox
2010/12/30 Javascript
js修改input的type属性及浏览器兼容问题探讨与解决
2013/01/23 Javascript
jQuery遍历Form示例代码
2013/09/03 Javascript
javascript窗口宽高,鼠标位置,滚动高度(详细解析)
2013/11/18 Javascript
jquery实现在页面加载完毕后获取图片高度或宽度
2014/06/16 Javascript
javascript中Date对象的getDay方法使用指南
2014/12/22 Javascript
JS判断网页广告是否被浏览器拦截过滤的代码
2015/04/05 Javascript
jQuery实现的仿select功能代码
2015/08/19 Javascript
jquery实现初次打开有动画效果的网页TAB切换代码
2015/09/06 Javascript
理解javascript中DOM事件
2015/12/25 Javascript
Javascript实现一个简单的输入关键字添加标签效果实例
2017/06/01 Javascript
npm国内镜像 安装失败的几种解决方案
2017/06/04 Javascript
JS对象序列化成json数据和json数据转化为JS对象的代码
2017/08/23 Javascript
node.js中路由,中间件,ge请求和post请求的参数详解
2017/12/26 Javascript
vue favicon设置以及动态修改favicon的方法
2018/12/21 Javascript
JavaScript 九种跨域方式实现原理
2019/02/11 Javascript
优雅的将ElementUI表格变身成树形表格的方法步骤
2019/04/11 Javascript
VUE脚手架具体使用方法
2019/05/20 Javascript
微信小程序实现页面监听自定义组件的触发事件
2020/11/01 Javascript
[06:40]2014DOTA2西雅图国际邀请赛 DK战队巡礼
2014/07/07 DOTA
Python面向对象特殊成员
2017/04/24 Python
PyQt Qt Designer工具的布局管理详解
2019/08/07 Python
python super的使用方法及实例详解
2019/09/25 Python
Django学习之文件上传与下载
2019/10/06 Python
TensorFlow索引与切片的实现方法
2019/11/20 Python
几道Java和数据库的面试题
2013/05/30 面试题
金融管理毕业生求职信
2014/03/03 职场文书
招聘专员岗位职责
2014/03/07 职场文书
施工员岗位职责
2014/03/16 职场文书
在职证明书模板
2015/06/15 职场文书
pytorch model.cuda()花费时间很长的解决
2021/06/01 Python
OpenCV-Python实现怀旧滤镜与连环画滤镜
2021/06/09 Python