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语言的优雅之处
Jul 04 Python
跟老齐学Python之一个免费的实验室
Sep 14 Python
python3中str(字符串)的使用教程
Mar 23 Python
Python 判断 有向图 是否有环的实例讲解
Feb 01 Python
Python读取英文文件并记录每个单词出现次数后降序输出示例
Jun 28 Python
idea创建springMVC框架和配置小文件的教程图解
Sep 18 Python
Python实现的爬取百度文库功能示例
Feb 16 Python
Python3之不使用第三方变量,实现交换两个变量的值
Jun 26 Python
PHP统计代码行数的小代码
Sep 19 Python
flask 框架操作MySQL数据库简单示例
Feb 02 Python
PyCharm安装PyQt5及其工具(Qt Designer、PyUIC、PyRcc)的步骤详解
Nov 02 Python
解决python绘图使用subplots出现标题重叠的问题
Apr 30 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开发的一些注意点总结
2010/10/12 PHP
Centos 6.5下PHP 5.3安装ffmpeg扩展的步骤详解
2017/03/02 PHP
基于Laravel实现的用户动态模块开发
2017/09/21 PHP
php gethostbyname获取域名ip地址函数详解
2010/01/24 Javascript
jQuery EasyUI API 中文文档 - Spinner微调器使用
2011/10/21 Javascript
如何防止回车(enter)键提交表单
2014/05/11 Javascript
js跨域访问示例(客户端/服务端)
2014/05/19 Javascript
node.js不得不说的12点内容
2014/07/14 Javascript
Jquery实现仿腾讯微博发表广播
2014/11/17 Javascript
解决js图片加载时出现404的问题
2020/11/30 Javascript
javascript闭包概念简单解析(推荐)
2016/06/03 Javascript
AngularJS 指令详细介绍
2016/07/27 Javascript
VC调用javascript的几种方法(推荐)
2016/08/09 Javascript
chrome浏览器如何断点调试异步加载的JS
2016/09/05 Javascript
JS中split()用法(将字符串按指定符号分割成数组)
2016/10/24 Javascript
详解基于angular路由的requireJs按需加载js
2017/01/20 Javascript
koa router 多文件引入的方法示例
2019/05/22 Javascript
javascript中可能用得到的全部的排序算法
2020/03/05 Javascript
JS制作简易计算器的实例代码
2020/07/04 Javascript
python list格式数据excel导出方法
2018/10/31 Python
解决python中画图时x,y轴名称出现中文乱码的问题
2019/01/29 Python
python如何爬取网站数据并进行数据可视化
2019/07/08 Python
python实现kNN算法识别手写体数字的示例代码
2019/08/16 Python
Python通过len函数返回对象长度
2020/10/22 Python
Python: glob匹配文件的操作
2020/12/11 Python
英国百年闻名的优质健康产品连锁店:Holland & Barrett
2019/12/19 全球购物
如何拷贝一整个Java对象,包括它的状态
2013/12/27 面试题
《中国的气候》教学反思
2014/02/23 职场文书
单位消防安全责任书
2014/07/23 职场文书
2014年高中生自我评价范文
2014/09/26 职场文书
地下停车场租赁协议范本
2014/10/07 职场文书
2014年监理工作总结范文
2014/11/17 职场文书
户外活动总结
2015/02/04 职场文书
2015初中政治教学工作总结
2015/07/21 职场文书
2016年元旦寄语
2015/08/17 职场文书
2019年描写人生经典诗句大全
2019/07/08 职场文书