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_基础_(面向对象进阶)的主要内容,如果未能解决你的问题,请参考以下文章

Python_基础_(面向对象进阶)

Python7 - 面向对象编程进阶

python语法基础-面向对象-进阶-长期维护

python - 面向对象编程基础知识 (进阶)

python基础_面向对象进阶

Python之路,Day8 - 面向对象编程进阶