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算法学习之桶排序算法实例(分块排序)
Dec 18 Python
由Python运算π的值深入Python中科学计算的实现
Apr 17 Python
python实现批量改文件名称的方法
May 25 Python
Python导入模块时遇到的错误分析
Aug 30 Python
python tensorflow学习之识别单张图片的实现的示例
Feb 09 Python
Python实现的计算器功能示例
Apr 26 Python
python3处理含有中文的url方法
May 10 Python
python使用正则表达式匹配txt特定字符串(有换行)
Dec 09 Python
python单例模式的应用场景实例讲解
Feb 24 Python
快速一键生成Python爬虫请求头
Mar 04 Python
OpenCV项目实践之停车场车位实时检测
Apr 11 Python
python游戏开发Pygame框架
Apr 22 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
《OVERLORD》手游英文版即将上线 手机上也能扮演骨王
2020/04/09 日漫
php 全局变量范围分析
2009/08/07 PHP
php 生成WML页面方法详解
2009/08/09 PHP
APACHE的AcceptPathInfo指令使用介绍
2013/01/18 PHP
php动态变量定义及使用
2015/06/10 PHP
如何用PHP来实现一个动态Web服务器
2015/07/29 PHP
yii2使用ajax返回json的实现方法
2016/05/14 PHP
PHP常用文件操作函数和简单实例分析
2016/06/03 PHP
通过js动态操作table(新增,删除相关列信息)
2012/05/23 Javascript
javascript的offset、client、scroll使用方法详解
2012/12/25 Javascript
javascript时间自动刷新实现原理与步骤
2013/01/06 Javascript
Jquery+CSS3实现一款简洁大气带滑动效果的弹出层
2013/05/15 Javascript
关于function类中定义变量this的简单说明
2016/05/28 Javascript
Ionic默认的Tabs模板使用实例
2016/08/29 Javascript
flexslider.js实现移动端轮播
2017/02/05 Javascript
解决angular双向绑定无效果,ng-model不能正常显示的问题
2018/10/02 Javascript
基于vue框架手写一个notify插件实现通知功能的方法
2019/03/31 Javascript
详解JavaScript 为什么要有 Symbol 类型?
2019/04/03 Javascript
Python中使用Inotify监控文件实例
2015/02/14 Python
在Python中使用SimpleParse模块进行解析的教程
2015/04/11 Python
深入理解 Python 中的多线程 新手必看
2016/11/20 Python
使用python 和 lint 删除项目无用资源的方法
2017/12/20 Python
Python3转换html到pdf的不同解决方案
2019/03/11 Python
解决python中使用PYQT时中文乱码问题
2019/06/17 Python
详解pyinstaller selenium python3 chrome打包问题
2019/10/18 Python
Python中six模块基础用法
2019/12/08 Python
基于Python爬虫采集天气网实时信息
2020/06/05 Python
Visual Studio Code搭建django项目的方法步骤
2020/09/17 Python
python 实现关联规则算法Apriori的示例
2020/09/30 Python
用Python制作音乐海报
2021/01/26 Python
计算机维护专业推荐信
2014/02/27 职场文书
开发房地产协议书
2014/09/14 职场文书
2014年煤矿安全工作总结
2014/12/04 职场文书
数据结构课程设计心得体会
2016/01/15 职场文书
民警忠诚教育心得体会
2016/01/23 职场文书
Redis高并发缓存架构性能优化
2022/05/15 Redis