1.类相关属性

1.1 类相关属性整理

特殊方法含义
obj.__class__对象所属的类
class.__bases__类的基类元组
class.__base__类的基类
class.__mro__类层次结构
class.__subclass__()子类类表

1.2 类相关属性操作

示例代码:

class A:
    class_attr1 = "class_val1"

    def __init__(self, attr1, attr2):
        self.attr1 = attr1
        self.attr2 = attr2

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass

a1 = A("val_a_1", "val_a_2")
print("所属的类")
print(a1.__class__)
print("--" * 20)

print("基类元组,针对多继承的情况")
print(D.__bases__)
print("--" * 20)

print("类的基类")
print(D.__base__)
print("--" * 20)

print("类完整层次结构")
print(D.__mro__)
print('--' * 20)

print("直接子类列表")
print(A.__subclasses__())

运行结果:


所属的类
<class '__main__.A'>
----------------------------------------
基类元组,针对多继承的情况
(<class '__main__.B'>, <class '__main__.C'>)
----------------------------------------
类的基类
<class '__main__.B'>
----------------------------------------
类完整层次结构
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
----------------------------------------
直接子类列表
[<class '__main__.B'>, <class '__main__.C'>]

代码说明:
类继承关系

  1. 按照上面类继承关系示意图创建类A、类B、类C、类D
  2. __class__代表对象所属的类,对象a1是类A的实例,所以a1.__class__对应的结果是:<class '__main__.A'>
  3. 因为python支持多继承,__bases__只返回直接继承父类的元组,定义类D过程中指定D(B, C),所以对应结果为:(<class '__main__.B'>, <class '__main__.C'>)
  4. __base__返回直接父类,可以使用在单继承和多继承中,对于多继承情况下是不准确的,只会返回第一个继承的类,定义类D过程中指定D(B, C),其中第一个类为B,所以返回:<class '__main__.B'>
  5. __mro__属性用于返回从当前类到类object的完整继承关系,对应结果的类型是tuple。对于多继承多分支情况,有兴趣的同学可以测试一下,采用什么规则
  6. __subclasses__()用于返回直接子类列表,在程序中,类B继承类A, 类C继承类A,所以对应的子类列表为:[<class '__main__.B'>, <class '__main__.C'>]

2.普通实例属性相关操作

实例属性相关特殊方法和特殊属性

方法说明例子
__dict__对象的属性字典a.__dict__
__getattr__获取属性a.attr_name
__setattr__属性赋值a.attr_name = value
__getattribute__获取属性a.attr_name
hasattr()判断属性是否存在hasattr(a, 'name')
getattr()获取属性getattr(a, 'name')
setattr()设置属性setattr(a, 'name') = 'niefajun'

正常情况下,对于对象的实例属性可以直接操作,以上的属性或者方法是对操作

示例代码:

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

    def __setattr__(self, key, value):
        print('__setattr__:key={0},value={1}'.format(key, value))
        super(Person, self).__setattr__(key, value)

    def __getattr__(self, item):
        print('__getattr__')
        print("获取的属性不存在")

    def __getattribute__(self, item):
        print("__getattribute__")
        return super(Person, self).__getattribute__(item)


p1 = Person("聂发俊", 100)
print('--' * 20)

print("属性字典")
print(p1.__dict__)
print('--' * 20)

print("判断属性是否存在")
has_flag = hasattr(p1, "name")
print(has_flag)
print('--' * 20)

print("设置属性1-直接设置")
p1.age = 101
print('--' * 20)

print('设置属性2-setattr函数')
setattr(p1, 'age', 102)
print('--' * 20)

print("设置属性3-__setattr__()方法")
p1.__setattr__('age', 103)
print('--' * 20)

print("获取属性1-直接获取")
print(p1.age)
print('--' * 20)

print("获取属性2-getattr函数")
print(getattr(p1, 'age'))
print('--' * 20)

print("获取属性3-__getattribute__方法")
print(p1.__getattribute__('age'))
print('--' * 20)

print("获取不存在的属性")
print(p1.name1)

运行结果:

__setattr__:key=name,value=聂发俊
__setattr__:key=age,value=100
----------------------------------------
属性字典
__getattribute__
{'name': '聂发俊', 'age': 100}
----------------------------------------
判断属性是否存在
__getattribute__
True
----------------------------------------
设置属性1-直接设置
__setattr__:key=age,value=101
----------------------------------------
设置属性2-setattr函数
__setattr__:key=age,value=102
----------------------------------------
设置属性3-__setattr__()方法
__getattribute__
__setattr__:key=age,value=103
----------------------------------------
获取属性1-直接获取
__getattribute__
103
----------------------------------------
获取属性2-getattr函数
__getattribute__
103
----------------------------------------
获取属性3-__getattribute__方法
__getattribute__
__getattribute__
103
----------------------------------------
获取不存在的属性
__getattribute__
__getattr__
获取的属性不存在
None

程序说明:
1.实例化过程中输出__setattr__:key=name__setattr__:key=age,这个是因为在__init__()方法中设置了这两个属性,都会经过__setattr__()方法,所以会执行两次。
2.属性字典中,本质上还是通过__getattribute__,所以也会输出__getattribute__的提示信息。
3.三种设置属性方法,比较特殊的是第三次,相比其他两次增加了__getattribute__输出,因为有一次判断属性是否存在的过程。
4.三种获取属性值,比较特殊的也是第三次,多了一次__getattribute__,和__setattr__()方法一样,增加了一次属性是否存在的过程,所以看起来比较特殊。
5.当获取一个不存在的属性,首先调用__getattribute__(self, item),如果属性存在则直接返回,不存在时,判断__getattr__(self)是否存在,存在的话,则执行__getattr__(self)方法,作为获取不存在属性的一种兜底操作。如果没有存在__getattr__(self),则直接提示属性异常。

__getattribute__(self, item) VS __getattr__(self, item):
相同点:都是用于属性获取
不同点:__getattribute__()无论属性是否存在都会首先访问,获取对应值;__getattr__()只有当属性不存在的时候,才会调用,作为兜底(异常)处理。

注意事项:对于__setattr__()__getattribute__()不要在函数里面直接赋值和返回属性,这样操作的话,又会调用__setattr__()__getattribute__(),构成死循环。
错误代码:

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

    def __setattr__(self, key, value):
        """错误操作"""
        # 等效操作: self.__setattr__(key, value), 自己调用自己,造成死循环
        self.key = value

    def __getattribute__(self, item):
        """错误操作"""
        # 等效操作: self.__getattribute__(item), 自己调用自己,造成死循环
        return self.item

3.对象操作

对象操作整理:

方法说明例子
__getitem__通过索引获取a[key_index]
__setitem__通过索引设置a[key_index] = value

在日常的开发过程中,使用的比较少

示例代码:

class Animal:
    def __init__(self, animal_li):
        self.animals_name = animal_li

    def __getitem__(self, item):
        return self.animals_name[item]


animals = Animal(['cat', 'dog', 'tiger'])
for animal in animals:
    print(animal)

执行结构:

cat
dog
tiger

注:使用的很少,等后面有具体使用场景在补充完善。


备注:
更多精彩博客,请访问:聂发俊的技术博客
对应视频教程,请访问:python400
完整markdown笔记,请访问: python400_learn_github