08-07 内置方法

一. 什么是内置方法?

"""
什么是内置方法?
    定义在类内部, 以__开头 并以__结尾的方法
    特点: 会在某种情况下自动触发执行
为什么要用内置方法?
    为了高度定制化我们的类 或者 对象.
"""

二. __str__

"""
在打印对象时自动触发,然后将返回值(必须是字符串类型)当做本次打印的结果输出
作用: 一般基于该方法来定制对象的打印信息,
"""


class People:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __str__(self):
        return f'姓名:{self.name} 年龄:{self.age}岁 性别:{self.sex}'


obj = People('egon', 18, '男')
print(obj)

三. __del__

'''
会在对象被清理之前, 会自动触发(提示: 程序运行完毕, 对象就会被GC回收, 这种清理之前, 会自动触发__del__方法的运行)
作用: 一般基于该方法来回收操作系统资源的操作.
    由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制__del__方法,但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下,关于系统资源的回收,Python的垃圾回收机制便派不上用场了,需要我们为对象定制该方法,用来在对象被删除时自动触发回收系统资源的操作
'''


class Open:
    def __init__(self, my_mode, file='a.txt'):
        self.f = open(file, mode=my_mode)  # open()这里占据的是操作系统资源

    def __del__(self):
        print("正在回收f句柄对象所占用的系统资源.......")
        self.f.close()  # 发起系统调用,告诉操作系统回收相关的系统资源
        print("f句柄对象所占用的系统资源回收完毕.......")


Open('wb')

四. isinstance(obj,cls) 和 issubclass(sub,super)

# isinstance(obj,cls)检查是否obj是否是类 cls 的对象
print(isinstance(1, str))    # False
print(isinstance('1', str))  # True


# issubclass(sub, super)检查sub类是否是 super 类的派生类
print(isinstance(type, object))  # True
print(issubclass(int, dict))     # False

五. __setattr__, __getattr__, __delattr__

'''
注意: 以下的自动触发的前提是操作对象达到某一条件, 自动触发其类中定义的方法. 
    同理, 类也是对象. 如果实例化类的元类中定义了以下的方法, 那么操作类达到某一条件, 也会自动触发其元类中定义的以下的方法.
__setattr__: 在添加或者修改属性的时候会自动触发. 
__getattr__: 在`对象.属性`不存在时会自动触发
__delattr__: 在删除属性的时候会自动触发       
'''
class Foo:
    x = 1

    def __init__(self, y):
        self.y = y

    def __getattr__(self, item):
        print('----> from getattr:你找的属性不存在')

    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key=value  # 这就无限递归了, 因为这里是对象的添加/修改操作, 又会触发__setattr__方法了.
        # self.__dict__[key]=value # 应该使用它

    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item  # 无限递归了
        self.__dict__.pop(item)    # 应该使用它


# __setattr__: 添加/修改属性会触发它的执行
f1 = Foo(10)
print(f1.__dict__)  # {}  为什么这里是空的? 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z = 3
print(f1.__dict__)  # {}

# __delattr__: 删除属性的时候会触发
f1.__dict__['a'] = 3  # 我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)  # {}

# __getattr__: 只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx    # 会触发. 因为xxxxxx属性不存在
print(f1.x)  # 不会触发

拓展: 继承dict类, 实现字典类型可以使用'对象.属性'操作获取值 以及 通过 '对象.属性=值'设置值

class MyDict(dict):
    def __setattr__(self, key, value):
        print("对象加点赋值,会触发我")
        self[key] = value

    def __getattr__(self, item):
        print("对象加点取值,会触发我")
        return self[item]  # 不要加引号


my_dict = MyDict(name='lqz', age=18)

print(my_dict['name'])  # 对象加点取值,会触发我


print(my_dict.name)
'''
lqz
对象加点赋值,会触发我
'''

my_dict.name = 99      # 对象加点赋值,会触发我

print(my_dict.name)
'''
对象加点取值,会触发我
99
'''

六. __getattribute__

1. __getattr__

class Foo:
    def __init__(self, x):
        self.x = x

    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]


f1 = Foo(10)
print(f1.x)  # 10
f1.xxxxxx    # 不存在的属性访问,触发__getattr__

2. __getattribute__

class Foo:
    def __init__(self, x):
        self.x = x

    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')


f1 = Foo(10)
f1.x
f1.xxxxxx

3. __getattr____getattribute__

当__getattribute__与__getattr__同时存在,只会执行__getattribute__,除非__getattribute__在执行过程中抛出异常AttributeError.

class Foo:
    def __init__(self, x):
        self.x = x

    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]

    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')
        raise AttributeError('哈哈')


f1 = Foo(10)
f1.x
print('======>')
f1.xxxxxx
'''
不管是否存在,我都会执行
执行的是我
======>
不管是否存在,我都会执行
执行的是我
'''

七. __setitem__,__getitem,__delitem__

class Foo:
    def __init__(self, name):
        self.name = name

    def __getitem__(self, item):
        # print(self.__dict__[item])  # 方式一
        print(getattr(self, item))    # 方式二

    def __setitem__(self, key, value):
        print("obj[key]=value时,我执行")
        # self.__dict__[key] = value  # 方式一
        setattr(self, key, value)     # 方式二

    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        # self.__dict__.pop(key)      # 方式一
        delattr(self, key)            # 方式二

    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)
        # delattr(self, item)         # 注意: 这里如果这样使用就递归了


f1 = Foo('sb')

# 自动触发__setitem__
f1['age'] = 18       # obj[key]=value时,我执行
f1['age1'] = 19      # obj[key]=value时,我执行

# 自动触发__delattr__
del f1.age1          # del obj.key时,我执行

# 先自动触发__delitem__, 再触发__delattr__
del f1['age']   
'''
del obj[key]时,我执行
del obj.key时,我执行
'''

# 自动触发__setitem__
f1['name'] = 'alex'   # obj[key]=value时,我执行

print(f1.__dict__)    # {'name': 'alex'}

八. __doc__

class Foo:
    """我是描述信息"""
    pass

  
print(Foo.__doc__)  # 我是描述信息

该属性无法继承给子类

class Foo:
    """我是描述信息"""
    pass


class Bar(Foo):
    pass


# 该属性无法继承给子类
print(Bar.__doc__)  # None 

九. __module__和__class__

__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么

class C:
    def __init__(self):
        self.name = 'SB'


c = C()
print(c.__module__)   # 输出 lib.aa,  即:输出模块
print(c.__class__)    # 输出 lib.aa.C,即:输出类
from lib.aa import C
c = C()
print(c.__module__)   # 输出 lib.aa,  即:输出模块
print(c.__class__)    # 输出 lib.aa.C,即:输出类

十. __enter__和__exit__

1. 上述叫做上下文管理协议,即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)

2. __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:", exc_type)
        print('exc_val:', exc_val)
        print('exc_tb:', exc_tb)


with Open('a.txt') as f:  # self.name='a.txt'
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0' * 100)  # ------------------------------->不会执行

# 执行结果
'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
exc_type: <class 'AttributeError'>
exc_val: ***着火啦,救火啊***
exc_tb: <traceback object at 0x000001E61C2DA908>
'''

3. 如果__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:", exc_type)
        print('exc_val:', exc_val)
        print('exc_tb:', exc_tb)
        return True


with Open('a.txt') as f:  # self.name='a.txt'
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0' * 100)  # ------------------------------->会执行

# 执行结果
'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
exc_type: <class 'AttributeError'>
exc_val: ***着火啦,救火啊***
exc_tb: <traceback object at 0x000001E61C2DA908>
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
'''

十一. __eq__, __lt__, __gt__

class A:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, obj):
        # 打印出比较的第二个对象的x值
        print(obj.x)
        if self.x + self.y == obj.x + obj.y:
            return True
        else:
            return False


a = A(1, 2)
b = A(99, 3)

# 当执行==s时,会触发__eq__的执行,并且把b传进去,就是object
# ==后只要是对象,就可以传进去,就是object
print(a == b)  # False

十二. 总结

__new__:  类实例化时自动触发. 在__init__之前. (例子: 造出裸体的人)
__init__: 类实例化后自动触发, 在__new__之后.  (例子: 穿衣服)
__call__: 调用对象自动触发其类 或者 父类中定义的__call__方法

__str__:  打印对象时自动触发
__del__:  对象被回收时自动触发

__setattr__: 添加/修改属性时自动触发
__getattr__: `对象.属性`不存在时自动触发
__delattr__: 删除属性时自动触发

__getattribute__: `对象.属性`存在或者不存在都触发.
    如果和__getattr__同时存在, 只会执行__getattribute__. 
    如果__getattribute__方法在执行过程中遇见异常, 会跑到__getattr__方法中执行. 如果__getattr__方法没有定义, 就只能抛出异常 

__getitem__: `对象['属性']`    时自动触发
__setitem__: `对象['属性']=值` 时自动触发
__delitem__: `del ['属性']`   时自动触发

__enter__: with操作时自动触发, 返回值赋值给as后面的句柄对象
__exit__:  with子代码块执行完毕以后自动触发. 
    三个参数: exc_type, exc_val, exc_db
    第一个参数: 异常类型
    第二个参数: 异常值
    第三个参数: 异常追随信息
    如果返回True则在with语句执行的过程中抛出异常不会影响with之后的语句的正常执行
    

__eq__: ==比较时自动触发. 等于号右边对象当作参数传入.
__lt__, __gt__

__class__:  获取当前类
__module__: 获取当前模块

__doc__: 获取文档描述. (提示: 子类继承, 无法获取)

十三. 拓展: 什么是构造方法? 什么是析构方法?

__init__: 构造方法
__del__:  析构方法

十四. 更多

https://www.cnblogs.com/linhaifeng/articles/6204014.html

https://www.cnblogs.com/liuqingzheng/articles/9949568.html#_label3

posted @ 2020-04-15 22:01  给你加马桶唱疏通  阅读(190)  评论(0编辑  收藏  举报