Python_基础_(面向对象进阶)
Posted 一头牛这么多人放
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python_基础_(面向对象进阶)相关的知识,希望对你有一定的参考价值。
一,isinstance(obj,cls) issubclass(sub,super)
isinstance(obj,cls) # 判断对象obj是否是cls的一个实例
class Test: pass t = Test() print(isinstance(t,Test)) # True
issubclass(sub,super) # 判断类sub是否为类super的子类
class Test: pass class Sub(Test): pass print(issubclass(Sub,Test)) # True s = Sub() print(isinstance(s,Sub)) # True print(isinstance(s,Test)) # True
type(f) # 可以用来看对象f使用哪个类产生的
class Foo: pass f = Foo() print(type(f)) # <class ‘__main__.Foo‘>
....
二,__getattribute__
## 当 __getattr__和 __getattribute__同时出现时
class Test: def __init__(self,name): self.name = name def __getattr__(self,item): print("执行的是__getattr__") def __getattribute__(self,item): print("执行的是__getattribute__") t = Test("henry") t.name # 当调用存在的属性:触发 __getattribute__ 不触发 __getattr t.xxxxxx# 当调用不存在的属性:触发 __getattribute__
## __getattribute__ 抛出异常时
class Test: def __init__(self,name): self.name = name def __getattr(self,item): print("执行的是__getattr__") def __getattribute__(self,item): print("执行的是__getattribute__") raise AttributeError("抛出一个属性异常") t = Test("henry") t.xxxxxx # 执行 __getattribute__时会抛出一个属性异常,不会直接报错,而后会去执行 __getattr__ ## 输出 执行的是__getattribute__ 执行的是__getattr__ # 注:当抛出的异常不是AttributeError,不会去执行 __getattr__ ,而是直接终止程序
...
三,__setitem__ __getitem__ __delitem__
# 字典形式的操作属性就是和item相关
# 点的形式操作属性,就是和内置方法相关
class Test: def __setitem__(self,key,value): parint("__sttitem__") self.__dic__[key] = value def __getitem__(self,item): parint("__getitem__") def __delitem__(self,key): print("__delitem__") self.__dic__.pop(key) ## 添加 t = Test() t["name"] = "henry" # 添加一个属性和值,会触发 __setitem__ t["user"] = "alex" print(t.__dic__) # {"name":"henry"} ## 删除 del t.name # 不会触发item操作 只是普通的操作 print(t.__dic__) # {} del t["name"] # 会触发__delitem__ print(t.__dic__) # {} ## 获取 t.user # 不会触发getitem操作 t["user"] # 会触发__getitem__
...
四,__str__ __repr__
# 当打印一个实例化的对象时
class Test: pass t = Test() print(t) # <__main__.Test object at 0x00000299CE10EBA8>
# __str__ 可以控制打印实例化的对象时的信息
class Test: def __str__(self): return "我是实例化的对象打印的信息" t = Test() print(t) # 我是实例化的对象打印的信息
# __repr__ 也是用于控制输出,应用于解释器中(cmd)
class Test: def __repr__(self): return "我是实例化的对象打印的信息" t = Test() print(t) # 我是实例化的对象打印的信息
# 注意
## 两者共存时,优先选择str
## 如果__str__没有被定义,那么就会用__repr__来替代输出
## str 和 repr 输出的结果返回的值都为字符串类型
class Test: def __str__(self): return "自定义的对象显示方式str" def __repr__(self): return "自定义的对象显示方式repr" t = Test() print(t) # 输出:自定义的对象显示方式str
...
五,自定义格式化 __format__
class Test: def __init__(self,year,mon,day): self.year = year self.mon = mon self.day = day t = Test(2018,12,22) x = "{0.year},{0.mon},{0.day}".format(t) y = "{0.year}:{0.mon}:{0.day}".format(t) z = "{0.year}-{0.mon}-{0.day}".format(t) print(x) # 2018,12,22 print(y) # 2018:12:22 print(z) # 2018-12-22
# 当上方的程序要换一种日期输出格式时,必须得用户来定义,这必然是不行的
format_dic = { "ymd":"{0.year},{0.mon},{0.day}", "y:m:d":"{0.year}:{0.mon}:{0.day}", "y-m-d":"{0.year}-{0.mon}-{0.day}" } class Test: def __init__(self, year, mon, day): self.year = year self.mon = mon self.day = day def __format__(self, format_spec): if not format_spec or format_spec not in format_dic: # 当用户没有输入格式时,程序自己选择为ymd # 当用户输入的格式不存在时 format_spec = "ymd" fm = format_dic[format_spec] return fm.format(self) # 相当于"{0,year}-{0,mon}-{0,day}",format(t) t = Test(2018, 12, 22) format(t) # 相当于执行了 t.__format__() print(format(t, "ymd")) # 2018,12,22 print(format(t, "y:m:d")) # 2018:12:22 print(format(t, "y-m-d")) # 2018-12-22
...
六,__slots__
1:__slots__:是一个变量,变量的值可以是列表,元组,字符串,可迭代的对象
2:当用点来访问一个属性时,实际上是在访问类或者对象的__dict__属性字典(类的属性字典是共享的,可被每个实例对象访问;二实例的字典是独立的)
3:为何使用了__slots__:因为字典会占用大量的内存,使用了__slots__不能再给其添加属性,只能使用之前在__slots__中定义的
4:定义了__slots__后不在支持一些普通类的特性,比如继承
# 基本形式
class Test: __slots__ = "name" # __slots__ = [‘name‘,‘age‘] t = Test() t.name = "henry" t.age = 18 # 报错,当前 __slots__替代了 __dic__,不能添加该属性 # 限制创建属性 print(t.__slots__) # name print(Test.__slots__) # name
...
七,__doc__ __module__ __class__
# __doc__
# 注:该属性不能被继承 class Test: ‘我是文档注释‘ pass pring(Test.__doc__) # 不能被继承 class Test: ‘我是文档注释‘ pass class Foo(Test): pass print(Foo.__doc__) # 无法继承给子类
# __module__ __class__
# test1.py位于文件 lib下 class A: def __init__(self): self.name = name # 在test2.py文件中导入 test1.py模块 from lib.test1 import A obj = A() print(obj.__module__) # 输出lib.test1 # 输出模块 print(obj.__class__) # 输出 lib.test1.A # 输出类
...
八,__del__析构方法
1:当对象在内存中被释放时,自动触发执行
2:在Python中程序员无需关心内存的分配和释放,Python解释器会自动来执行,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的,该方法一般无需定义
# 当删除一个对象时,触发__del__
class Test: def __del__(self): print("执行了del方法") t = Test() del t # 删除对象 print("程序执行完毕") # 输出 执行了del方法 程序执行完毕
# 当程序执行完毕后,触发__del__
class Test: def __del__(self): print("执行了del方法") t = Test() print("程序执行完毕") # 输出 程序执行完毕 执行了del方法 # 在程序执行完成后,会执行垃圾回收机制
# 当删除一个属性时,不会触发__del__
# 当删除一个属性时,不会执行__del__方法 class Test: def __init__(self,name): self.name = name def __del__(self): print("执行了del方法") t = Test("henry") del t.name print("程序执行完毕") # 输出 程序执行完毕 执行了del方法
...
九,__call__
class Test: def __call__(self, *args, **kwargs): print("执行了__call__") t = Test() t() # 一个对象后加括号,触发执行__call__
# 一切皆对象,假如Test类由类Foo产生,则执行Test(),相当于执行了类Foo下的__call__方法
十,__next__ __iter__
..待完成
十一,描述符的基本概况
# 描述符:本质就是在一个新式类中,这个新式类中至少实现 __get__(),__set__(),__delete__()中的一个,这三个也被称为描述符协议
__get__() # 调用一个属性时触发
__set__() # 给一个属性赋值时触发
__delete__() # 利用del删除属性时触发
# 什么情况下会触发着三个方法(以下为错误示范)
class Test: def __init__(self,name,age): self.name = name self.age = age def __set__(self, instance, value): print("__set__") def __get__(self, instance, owner): print("__get__") def __delete__(self, instance): print("__delete__") t = Test("henry",18) t.name # 没调用 t.name = "alex" # 没调用 del t.name # 没调用
# 什么情况下会触发这三个方法(以下为正确示范)
class Test: def __get__(self, instance, owner): print("触发__get__") def __set__(self, instance, value): print("触发__set__") def __delete__(self, instance): print("触发__delete__") class Bar: name = Test() # 数据描述符 # 表示一个描述符 所有与name有关的操作都去找类Test # 表示在类Bar中利用类Test来描述name属性 def __init__(self,name): # name被Test()代理 self.name = name b = Bar("henry") # 触发__set__,instance为b,value为henry b.name # 触发__get__ b.name = "heihei" # 触发__set__ del b.name # 触发__delete__ print(b.__dict__) # {} print(Bar.__dict__) # ‘name‘: <__main__.Test object at 0x0000017E78719470>
class Test_1: def __get__(self, instance, owner): print("Test_1触发__get__") def __set__(self, instance, value): print("Test_1触发__set__") def __delete__(self, instance): print("Test_1触发__delete__") class Test_2: def __get__(self, instance, owner): print("Test_2触发__get__") def __set__(self, instance, value): print("Test_2触发__set__") def __delete__(self, instance): print("Test_2触发__delete__") class Bar: name = Test_1() age = Test_2() def __init__(self,name,age): # name被Test_1()代理,age被Test_2()代理 self.name = name self.age = age b = Bar("henry",18) # Test_1触发__set__ # Test_2触发__set__ # Test_1 b.name b.name = "heihei" del b.name # Test_1触发__get__ # Test_1触发__set__ # Test_1触发__delete__ # Test_2 b.age b.age = 19 del b.age # Test_2触发__get__ # Test_2触发__set__ # Test_2触发__delete__ print(b.__dict__) # {} print(Bar.__dict__) # ‘name‘: <__main__.Test_1 object at 0x0000021EF9F7F780>, # ‘age‘: <__main__.Test_2 object at 0x0000021EF9F7FCC0>,
# 数据描述符和非数据描述符
# 数据描述符 class Foo: def __set__(self, instance, value): print(‘set‘) def __get__(self, instance, owner): print(‘get‘) # 二 非数据描述符:没有实现__set__() class Foo: def __get__(self, instance, owner): print(‘get‘)
# 描述符的注意事项
一:描述符不本身应该定义成新式类,被代理的类也应该时形式类
二:必须把描述符定义成则个类的类属性,不能为定义到构造函数中
三:要严格遵循优先级
1,类属性
2,数据描述符 # 具有 __get__()和__set__()
3,实例属性
4,非数据描述符 # 没有__set__()
5,找不到属性触发 __getattr__()
# 描述符优先级问题
## 类属性>数据描述符
class Test: def __get__(self, instance, owner): print("调用get") def __set__(self, instance, value): print("调用set") def __delete__(self, instance): print("调用delete") class Foo: name = Test() def __init__(self,name): self.name = name Foo.name # # 输出:调用get # 在Foo的属性字典中,‘name‘: <__main__.Test object at 0x00000262B7BF97B8>, # 当调用属性name时,属性name为描述符伪装成类属性name,当在类中找不到属性对饮的值,就到类Test中,触发了__get__() Foo.name = "henry" # 直接赋值类的一个属性,因为类属性拥有更高的优先级,相当于覆盖了描述符,不会触发描述符中的__set__() print(Foo.__dict__) # "name":"henry" del Foo.name # 删除的为类中的类属性,没有触发__delete__()
## 数据描述符>实例属性
class Test: def __get__(self, instance, owner): print("调用get") def __set__(self, instance, value): print("调用set") def __delete__(self, instance): print("调用delete") class Foo: name = Test() def __init__(self,name): self.name = name f = Foo("henry") print(f.__dict__) # {} # 实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除/都和实例无关 f.name = "heihei" # 输出:调用set # 先在自己的实例属性中找,找不到则到类中找 # f.name的调用与赋值都是触发描述符的操作,与f本身无法无关,相当于覆盖了实例属性 del f.name # 调用delete
## 实例属性>非数据描述符
class Test: def __get__(self, instance, owner): print("调用get") # def __delete__(self, instance): # print("调用delete") class Foo: name = Test() # 变成了非数据描述符 # name是一个非数据描述符,因为name=Test() 而Test()没有实现set方法,因而比实例属性由更低的优先级 f = Foo() f.name # 调用get f.name = "henry" # 相当于在类Foo中进行设置属性的值 print(f.__dict__) # {‘name‘: ‘henry‘} # 实例属性的优先级高于非数据描述符,当描述符中没有set时,就在自己的实例上添加属性
...
十二,描述符的应用
# 描述符中的参数
class Test: def __get__(self, instance, owner): print("执行get") print("instance参数为:%s" %instance) print("owner参数为:%s" % owner) def __set__(self, instance, value): print("执行set") print("instance参数为:%s" % instance) print("value参数为:%s" % value) def __delete__(self, instance): print("执行delete") print("instance参数为:%s" % instance) class Foo: name = Test() def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary f = Foo("henry",18,10.1) # 执行set # instance参数为:<__main__.Foo object at 0x000001F1E436E748> # value参数为:henry f.name # 执行get # instance参数为:<__main__.Foo object at 0x000001F1E436E748> # owner参数为:<class ‘__main__.Foo‘> print(f.__dict__) # name不存在对象f的字典中 # {‘age‘: 18, ‘salary‘: 10.1}
# 简单操作
class Test: def __init__(self,key): self.key = key def __get__(self, instance, owner): print("执行get") return instance.__dict__[self.key] # instance为对象f,owner为类Foo def __set__(self, instance, value): print("执行set") instance.__dict__[self.key] = value # 相当于给对象f添加键值对 def __delete__(self, instance): print("执行delete") instance.__dict__.pop(self.key) class Foo: name = Test("name") def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary f = Foo("henry",18,10.1) print(f.name) # henry print(f.__dict__) # {‘key‘: ‘henry‘, ‘age‘: 18, ‘salary‘: 10.1} f.name = "heihei" print(f.__dict__) # {‘key‘: ‘heihei‘, ‘age‘: 18, ‘salary‘: 10.1} del f.name print(f.__dict__) # {‘age‘: 18, ‘salary‘: 10.1}
# 针对name赋值时的类型检查
class Test: def __init__(self,key): self.key = key def __get__(self, instance, owner): print("执行get") return instance.__dict__[self.key] # instance为对象f,owner为类Foo def __set__(self, instance, value): print("执行set") if not type(value) == str: # 判断类型 raise TypeError("你传入的不是字符串类型") instance.__dict__[self.key] = value def __delete__(self, instance): print("执行delete") instance.__dict__.pop(self.key) class Foo: name = Test("name") def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary f = Foo(123,18,10.1) 执行set Traceback (most recent call last): File "F:/Python_Project/Test/描述符的应用.py", line 26, in <module> f = Foo(123,18,10.1) File "F:/Python_Project/Test/描述符的应用.py", line 22, in __init__ self.name = name File "F:/Python_Project/Test/描述符的应用.py", line 12, in __set__ raise TypeError("你传入的不是字符串类型") TypeError: 你传入的不是字符串类型
# 针对name和age赋值时的类型检查
class Test: def __init__(self,key,expected_type): # expected_type想要检测的类型 self.key = key self.expected_type = expected_type def __get__(self, instance, owner): print("执行get") return instance.__dict__[self.key] # instance为对象f,owner为类Foo def __set__(self, instance, value): print("执行set") if not type(value) == self.expected_type: # 判断类型 raise TypeError("你传入的不是%s类型" %self.expected_type) instance.__dict__[self.key] = value def __delete__(self, instance): print("执行delete") instance.__dict__.pop(self.key) class Foo: name = Test("name",str) age = Test("age",int) def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary # f = Foo(123,18,10.1) # TypeError: 你传入的不是<class ‘str‘>类型 f = Foo("henry","18",10.1) # TypeError: 你传入的不是<class ‘int‘>类型
...
十三,__enter__ __exit__ 上下文管理协议
## 第一种文件操作(使用完得关闭文件) f = open("xxx","r") 文件操作 f.close ## 另一种文件操作(使用完不必关闭文件) with open("xxxx","r") as file "文件操作代码块"
# 上述称为上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
## 上下文管理协议
# 一 数据描述符:至少实现了__get__()和__set__() class Open: def __init__(self,name): self.name = name def __enter__(self): # 在with Open("a.txt") as f:执行时触发 print("执行了enter") return self def __exit__(self, exc_type, exc_val, exc_tb): # 等到代码块执行完成后执行 print("执行了exit") return self with Open("a.txt") as f: print("代码块执行完成") # 输出 执行了enter 代码块执行完成 执行了exit # Open("a.txt")的过程创建了一个对象,当执行with时执行了__enter__(),__enter__()返回一个值(self)赋值给f,f就是类Open产生的一个对象
## 当with中的代码块出现异常时
class Open: def __init__(self,name): self.name = name def __enter__(self): # 在with Open("a.txt") as f:执行时触发 print("执行了enter") def __exit__(self, exc_type, exc_val, exc_tb): # 等到代码块执行完成后执行 print("执行了exit") print(exc_type) print(exc_val) print(exc_tb) # return True 当在exit中返回一个True时,代表不抛出程序出现异常 with Open("a.txt") as f: print("代码块开始执行执行") print(abc) # 当在with中出现一个错误时 print("代码块执行完成") print("11111111111")
# 当不return True时的 输出的结果 执行了enter 代码块开始执行执行 执行了exit Traceback (most recent call last): <class ‘NameError‘> File "F:/Python_Project/Test/利用描述符定制property.py", line 17, in <module> name ‘abc‘ is not defined print(abc) # 当在with中出现一个错误时 <traceback object at 0x000001E2A2D59FC8> NameError: name ‘abc‘ is not defined
# 当return True时的结果 执行了enter 代码块开始执行执行 执行了exit <class ‘NameError‘> name ‘abc‘ is not defined <traceback object at 0x000001E205DB9F88> 11111111111
## exit中的三个参数
exc_type:异常类
exc_val:异常值
exc_tb:异常追踪信息
<class ‘NameError‘>
+
name ‘abc‘ is not defined
+
<traceback object at 0x0000016F7FAE9FC8>
=
NameError: name ‘abc‘ is not defined
## 总结
with obj as f:
"代码块"
1:with obj # 触发obj.__enter__(),获得返回值self
2:as f # 将__enter__返回值赋值给f,f = 返回值
3:with obj as f # 等同于 f = obj.__enter__()
4:执行代码块
1:在没有发生异常的情况下,整个代码块执行完成后触发__exit__,exit中的三个参数都为None
2:在有异常的情况下,从异常出现的位置直接触发__exit__
a:如果__exit__的返回值为True,代表吞掉异常,不抛出异常,程序不报错
b:如果__exit__的返回值不为True,表示抛出异常,程序报错
c:__exit__的运行完毕,表示整个with语句的执行完毕
...
以上是关于Python_基础_(面向对象进阶)的主要内容,如果未能解决你的问题,请参考以下文章