__item__系列: getitem setitem delitem 对对象做类似于字典的(增删改查)触发__item__系列
class Foo:
def init(self, name):
self.name = name
def getitem(self, item): # 在以对象名+['对象属性名'] 来模仿字典形式 dict[key] ##在以对象名+['对象属性名']时执行本函数
# print(item)
# print(666)
return self.dict[item] # 返回的是对象__dict__字典里的键(item)的值,这样写就实现功能了
def setitem(self, key, value): # 还是模仿字典,在以【对象名[key]=value】时执行,可以用这个方法为对象插入属性,属性名字必须是字符串(而且必须按照变量名命名规则命名)
self.dict[key] = value # 为了逼真点,在类的字典里加上一个键值对,这样写就可以真的添加了一个属性
# print(key)
# print(value)
# 删除时候执行del 对象['对象属性名’]
def delitem(self, key):
# print('del obj[key]时,我执行')
del self.dict[key] # 这样写就可以真的删除了
# del 对象.属性时候执行
def delattr(self, item):
super().delattr(item)
print(f'对象的{item}属性已经删除')
如果传入的是一个字符串那么就能实现切片的功能
f1 = Foo('sb')
print(f1['name'])
print(f1.dict)
f1['m23'] = 2
print(f1.dict)
print(f1.m23)
del f1['m23']
print(f1.dict)
print(Foo.dict)
################################################################################################
__delattr__系列:
class Person:
def init(self, name):
self.name = name
def setattr(self, key, value):# 当设置对象成员属性的时,系统会自动调用
print(key, value)
self.dict[key] = value
def getattr(self, item):# 当访问不存在的对象属性时,系统会自动调用
if item == 'age':
return 123
else:
return 'default'
def delattr(self, item):# 当销毁对象的成员属性时,系统会自动调用
print('del', item)
xiaoming = Person('1234')
# 每个对象都有一个成员属性:dict
# 用于存放对象的属性,包括动态添加的
print(xiaoming.dict)
xiaoming.name = '小明'
print(xiaoming.name)
print(xiaoming.dict)
xiaoming.age = 18
print(xiaoming.age)
print(xiaoming.hello)
del xiaoming.age
#######################################################################
delattr:
class Car:
refcount = 0
def init(self, power):
self.power = power
self.totaldistance = 0
self.dic = {1:2,2:3}
def delattr(self, name):
print(f"execute delattr:name={name}")
super().delattr(name) # 调用对象所属类的父类的__delattr__方法(也就是object类的__delattr__方法删除了属性name)
a = Car('卡车')
print(a.dict)
del a.power
print(a.dict)
del a.dict
# __dict__属性是删不掉的, 只能是清空
print(a.dict)
# {}
"""
执行结果:
{'power': '卡车', 'totaldistance': 0}
execute delattr:name=power
{'totaldistance': 0}
execute delattr:name=dict
{}
"""
enter __exit__成对出现
正常例子:
class A:
def init(self, text):
self.text = text
def enter(self): # 开启上下文管理器对象时触发此方法
self.text = self.text + '您来啦' # 第一步
print(11111)
return self # 必须!!!将实例化的对象返回f1
def exit(self, exc_type, exc_val, exc_tb): # 执行完上下文管理器对象f1时触发此方法
print(333) # 第三步
self.text = self.text + ',这就走啦'
def new(cls, *args, **kwargs):
print(44)
return super().new(cls) # 必须要有return,否则对象空间不会返回给__init__进而不会把属性传给地址
with A('大爷') as f1:
print(2222)
print(f1.text) # 第二步 (如果__enter__方法没有return self 那么不能执行 f1.text(因为这个创建还没完事) )
print(f1.text) # 第四步
扩展举例:
class Sample:
def enter(self):
return self
def exit(self, type, value, trace):
print ("type:", type)
print ("value:", value)
print ("trace:", trace)
def do_something(self):
bar = 1/0
return bar + 10
with Sample() as sample:
sample.do_something()
"""
执行结果:
C:Python36python.exe C:/python_text24/面向对象/双下划线方法.py
type: <class 'ZeroDivisionError'>
value: division by zero
trace: <traceback object at 0x000002A1AD781648>
Traceback (most recent call last):
File "C:/python_text24/面向对象/双下划线方法.py", line 104, in
sample.do_something()
File "C:/python_text24/面向对象/双下划线方法.py", line 100, in do_something
bar = 1/0
ZeroDivisionError: division by zero
"""
实际上,在with后面的代码块抛出任何异常时,exit()方法被执行。正如例子所示,异常抛出时,
与之关联的type,value和stack trace传给__exit__()方法,因此抛出的ZeroDivisionError异常被打印出来了。
开发库时,清理资源,关闭文件等等操作,都可以放在__exit__方法当中。
from collections import Iterable
class A:
def init(self,name):
self.name = name
def iter(self):
for i in range(10):
yield i
#因为没办法了解到__next__的方法的源码所以不知道咋整了
obj = A('123')
print(dir(obj))
print(isinstance(obj, Iterable))
for i in obj:
print(i)
这个是一个重构过的__next__方法的生成器
class Next(object):
def init(self, data=1):
self.data = data
def iter(self):
return self
def next(self):
print("next called")
if self.data > 5:
raise StopIteration
else:
self.data += 1
return self.data
a = Next()
for i in a:
print(i)
print(a.next())
print(a.next())
print(a.next())
print(a.next())
print(a.next())
print(a.next())
len ,hash ,eq 通过名字就知道咋回事,只要对象调用函数,则就找到对应的方法
str , repr 方法
print('对象')时候自动调用对象中的__str__(面向客户的) 或 __repr__方法(面向程序员的)( __str__方法的优先级高与 repr)
可以重构
call
对象后面加括号,触发执行。
注:构造方法__new__的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def init(self):
pass
def call(self, *args, **kwargs):
print('call')
obj = Foo() # 执行 init
obj() # 执行 call
先__new__ :创建一个对象时候先要执行此方法创造一个对象空间(用的对象的父类object的__new__方法),把地址传给__init__
class A:
__instance = None
def new(cls, *args, **kwargs):
if cls.__instance is None:
obj = object.new(cls)
cls.__instance = obj
return cls.__instance
"""
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。
通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,
从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
【采用单例模式动机、原因】
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;
一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。
如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;
如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。
因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。
一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。
这就是单例模式的模式动机。
【单例模式优缺点】
【优点】
一、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
二、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
【缺点】
一、开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
二、可能的开发混淆
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,
因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
三、对象生存期
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,
因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用"""
再__init__ :在对象地址里封装属性,
再再__del__ :在类在下面程序里不被调用时候执行__del__
################################################################
getattr(self, name):
定义当用户试图获取一个不存在的属性时的行为。这适用于对普通拼写错误的获取和重定向,
对获取一些不建议的属性时候给出警告(如果你愿意你也可以计算并且给出一个值)或者处理一个 AttributeError 。只有当调用不存在的属性的时候会被返回。
#################################################################
setattr(self, name, value):
与__getattr__(self, name)不同,setattr 是一个封装的解决方案。无论属性是否存在,它都允许你定义对对属性的赋值行为,
以为这你可以对属性的值进行个性定制。实现__setattr__时要避免"无限递归"的错误。
#################################################################
delattr:
与 setattr 相同,但是功能是删除一个属性而不是设置他们。实现时也要防止无限递归现象发生。
##################################################################
getattribute(self, name):
__getattribute__定义了你的属性被访问时的行为,相比较,__getattr__只有该属性不存在时才会起作用。因此,在支持__getattribute__的Python版本,
调用__getattr__前必定会调用 getattribute。getattribute__同样要避免"无限递归"的错误。需要提醒的是,最好不要尝试去实现__getattribute,
因为很少见到这种做法,而且很容易出bug。
在进行属性访问控制定义的时候很可能会很容易引起“无限递归”。如下面代码:
# 错误用法
def setattr(self, name, value):
self.name = value
# 每当属性被赋值的时候(如self.name = value), __setattr__()
会被调用,这样就造成了递归调用。
# 这意味这会调用 self.__setattr__('name', value)
,每次方法会调用自己。这样会造成程序崩溃。
# 正确用法
def setattr(self, name, value):
self.dict[name] = value # 给类中的属性名分配值
# 定制特有属性
#######################################################################