android 调用没有定义的so接口 调用的方法可能未定义_android 调用没有定义的so接口

1. 实例创建和销毁

  • __new__是一个静态方法,它会将所请求实例所属的类作为第一个参数,并返回一个cls类的新实例,是类级别的方法。允许不可变类型的子类(例如int,str,tuple)定制实例创建的过程,常会在自定义元类中被重载,以实现定制类的创建过程。
  • __init__在实例被创建之后,返回调用者之前调用。对象是由__new__创建,并由__init__进行定制,进行初始化,所以其返回值只能是None,否则会在运行时引发TypeError
    通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后,是实例级别的方法。
  • __del__对象被内存中销毁前,会被自动调用。del x 并不直接调用 x.__del__() ,前者会将 x 的引用计数减1,而后者仅会在 x 的引用计数变为0时被调用。
# 示例代码
class Cat:
    def __new__(cls, *args, **kwargs):
        print('__new__被调用了')
        return super().__new__(cls)

    def __init__(self, name):
        print('__init__被调用了')
        self.name = name

    def __del__(self):
        print('__del__被调用了')


if __name__ == '__main__':
    tom = Cat(name='Tom')
    jack = tom
    print(tom)
    del tom # 当引用次数不为0时,不会调用__del__
    print('-'*50)
    print(jack)

# 示例代码
class Cat:
    def __new__(cls, *args, **kwargs):
        print('__new__被调用了')
        return super().__new__(cls)

    def __init__(self, name):
        print('__init__被调用了')
        self.name = name

    def __del__(self):
        print('__del__被调用了')


if __name__ == '__main__':
    tom = Cat(name='Tom')
    jack = tom
    print(tom)
    del tom # 当引用次数不为0时,不会调用__del__
    print('-'*50)
    print(jack)
# 输出结果
__new__被调用了
__init__被调用了
<__main__.cat>0x0000018A72B48B80>
--------------------------------------------------<__main__.cat>0x0000018A72B48B80>
__del__被调用了
# 输出结果
__new__被调用了
__init__被调用了
<__main__.cat>0x0000018A72B48B80>
--------------------------------------------------<__main__.cat>0x0000018A72B48B80>
__del__被调用了

2. 集合模拟

  • __len__调用此方法以实现内置函数 len()。应该返回对象的长度,以一个 >=0  的整数表示。
  • __getitem__调用此方法以实现 self[key] 的求值。对于序列类型,接受的键应为整数和切片对象。请注意负数索引(如果类想要模拟序列类型)的特殊解读是取决于 __getitem__()方法。
  • __setitem__调用此方法以实现向 self[key] 赋值。为对象实现此方法应该仅限于需要映射允许基于键修改值或添加键,或是序列允许元素被替换时。
  • __delitem__调用此方法以实现 self[key] 的删除。为对象实现此方法应该权限于需要映射允许移除键,或是序列允许移除元素时。
  • __contains__调用此方法以实现成员检测运算符。如果 itemself 的成员则应返回真,否则返回假。对于映射类型,此检测应基于映射的键而不是值或者键值对。
# 示例代码
class CountList:
    def __init__(self, *args):
        self.values = [x for x in args]

    def __len__(self):
        # 调用`len`方法会触发
        return len(self.values)

    def __getitem__(self, item):
        # 调用`self[key]`进行取值会触发
        return self.values[item]

    def __setitem__(self, key, value):
        # 调用`self[key]`进行赋值会触发
        self.values[key] = value

    def __delitem__(self, key):
        # 调用`del`会触发
        self.values.pop(key)

    def __contains__(self, item):
        # 调用`in`会触发
        return item in self.values


if __name__ == '__main__':
    count_list = CountList(1, 2, 3, 4, 5, 6, 7)
    print("列表为:".format(count_list.values))
    print("列表长度为:{}".format(len(count_list)))
    print("列表中第3个元素为:{}".format(count_list[2]))
    print("-----修改第3个元素的值-----")
    count_list[2] = 10
    print("修改列表中第3个元素后为:{}".format(count_list.values))
    print("-----删除第3个元素的值-----")
    del count_list[2]
    print("删除列表中第3个元素后为:{}".format(count_list.values))
    print("-----判断3是否在列表中-----")
    print("3在列表中:{}".format(3 in count_list))
    print("4在列表中:{}".format(4 in count_list))

# 示例代码
class CountList:
    def __init__(self, *args):
        self.values = [x for x in args]

    def __len__(self):
        # 调用`len`方法会触发
        return len(self.values)

    def __getitem__(self, item):
        # 调用`self[key]`进行取值会触发
        return self.values[item]

    def __setitem__(self, key, value):
        # 调用`self[key]`进行赋值会触发
        self.values[key] = value

    def __delitem__(self, key):
        # 调用`del`会触发
        self.values.pop(key)

    def __contains__(self, item):
        # 调用`in`会触发
        return item in self.values


if __name__ == '__main__':
    count_list = CountList(1, 2, 3, 4, 5, 6, 7)
    print("列表为:".format(count_list.values))
    print("列表长度为:{}".format(len(count_list)))
    print("列表中第3个元素为:{}".format(count_list[2]))
    print("-----修改第3个元素的值-----")
    count_list[2] = 10
    print("修改列表中第3个元素后为:{}".format(count_list.values))
    print("-----删除第3个元素的值-----")
    del count_list[2]
    print("删除列表中第3个元素后为:{}".format(count_list.values))
    print("-----判断3是否在列表中-----")
    print("3在列表中:{}".format(3 in count_list))
    print("4在列表中:{}".format(4 in count_list))
# 输出结果
列表为:[1, 2, 3, 4, 5, 6, 7]
列表长度为:7
列表中第3个元素为:3
-----修改第3个元素的值-----
修改列表中第3个元素后为:[1, 2, 10, 4, 5, 6, 7]
-----删除第3个元素的值-----
删除列表中第3个元素后为:[1, 2, 4, 5, 6, 7]
-----判断3是否在列表中-----
3在列表中:False
4在列表中:True

# 输出结果
列表为:[1, 2, 3, 4, 5, 6, 7]
列表长度为:7
列表中第3个元素为:3
-----修改第3个元素的值-----
修改列表中第3个元素后为:[1, 2, 10, 4, 5, 6, 7]
-----删除第3个元素的值-----
删除列表中第3个元素后为:[1, 2, 4, 5, 6, 7]
-----判断3是否在列表中-----
3在列表中:False
4在列表中:True

3.字符串 /字节序列表示形式

  • __str__通过 str(object)以及内置函数 format()print()调用以生成一个对象的“非正式”或格式良好的字符串表示。返回值必须为一个 字符串 对象。类的实例打印时调用这个函数返回用户看到的
  • __repr__repr()内置函数调用以输出一个对象的“官方”字符串表示,返回值必须是一个字符串对象,返回开发者看到的。当__str__没有实现时,进行打印或者转换字符串,会调用__repr__
  • __format__通过 format() 内置函数、扩展、格式化字符串字面值 的求值以及 str.format()方法调用以生成一个对象的“格式化”字符串表示。format_spec参数为包含所需格式选项描述的字符串。format_spec参数的解读是由实现 __format__() 的类型决定的,不过大多数类或是将格式化委托给某个内置类型,或是使用相似的格式化选项语法。
  • __bytes__通过 bytes 调用以生成一个对象的字节串表示。这应该返回一个 bytes对象。
# 示例代码
class Cat:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        # 调用`print`,`str`会触发
        print("__str__被调用")
        return "Cat(name=" + str(self.name) + ",age=" + str(self.age) + ")"

    def __repr__(self):
        # 调用'repr'时会触发,当`__str__`没有实现,调用`print`,`str`会触发
        print("__repr__被调用")
        return self.name

    def __format__(self, format_spec):
        # 调用 `.format(self)`,`format`时会触发
        print("__format__被调用")
        if format_spec == '':
            return self.name
        return format_spec.replace("%s", self.name).replace("%r", self.age)

    def __bytes__(self):
        # 调用`bytes`时会触发
        print("__bytes__被调用")
        return bytes(self.name, encoding='utf-8')


if __name__ == '__main__':
    tom = Cat(name="Tom", age="18")
    print("-"*50)
    print(tom)
    str(tom)
    print("-"*50)
    repr(tom)
    print("-" * 50)
    "{}".format(tom)
    format(tom)
    print("-" * 50)
    bytes(tom)

# 示例代码
class Cat:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        # 调用`print`,`str`会触发
        print("__str__被调用")
        return "Cat(name=" + str(self.name) + ",age=" + str(self.age) + ")"

    def __repr__(self):
        # 调用'repr'时会触发,当`__str__`没有实现,调用`print`,`str`会触发
        print("__repr__被调用")
        return self.name

    def __format__(self, format_spec):
        # 调用 `.format(self)`,`format`时会触发
        print("__format__被调用")
        if format_spec == '':
            return self.name
        return format_spec.replace("%s", self.name).replace("%r", self.age)

    def __bytes__(self):
        # 调用`bytes`时会触发
        print("__bytes__被调用")
        return bytes(self.name, encoding='utf-8')


if __name__ == '__main__':
    tom = Cat(name="Tom", age="18")
    print("-"*50)
    print(tom)
    str(tom)
    print("-"*50)
    repr(tom)
    print("-" * 50)
    "{}".format(tom)
    format(tom)
    print("-" * 50)
    bytes(tom)
# 输出结果
--------------------------------------------------
__str__被调用
Cat(name=Tom,age=18)
__str__被调用
--------------------------------------------------
__repr__被调用
--------------------------------------------------
__format__被调用
__format__被调用
--------------------------------------------------
__bytes__被调用

# 输出结果
--------------------------------------------------
__str__被调用
Cat(name=Tom,age=18)
__str__被调用
--------------------------------------------------
__repr__被调用
--------------------------------------------------
__format__被调用
__format__被调用
--------------------------------------------------
__bytes__被调用

4.数值转换

  • __abs__调用此方法以实现内置函数 abs()
  • __bool__调用此方法以实现真值检测以及内置的 bool() 操作;应该返回 FalseTrue。如果未定义此方法,则会查找并调用 __len__() 并在其返回非零值时视对象的逻辑值为真。如果一个类既未定义 __len__()也未定义__bool__() 则视其所有实例的逻辑值为真。
  • __complex__调用此方法以实现内置函数 complex(),应当返回一个complex类型的值。
  • __int__调用此方法以实现内置函数 int(),应当返回一个int类型的值。
  • __float__调用此方法以实现内置函数float(),应当返回一个float类型的值。
  • __hash__通过内置函数 hash()调用以对哈希集的成员进行操作,属于哈希集的类型包括 setfrozenset 以及 dict__hash__()应该返回一个整数。对象比较结果相同所需的唯一特征属性是其具有相同的哈希值;建议的做法是把参与比较的对象全部组件的哈希值混在一起,即将它们打包为一个元组并对该元组做哈希运算。
  • __index__调用此方法以实现 operator.index() 以及 Python 需要无损地将数字对象转换为整数对象的场合(例如切片或是内置的 bin(), hex()oct() 函数)。存在此方法表明数字对象属于整数类型。必须返回一个整数。
    如果未定义 __int__(), __float__()__complex__()则相应的内置函数 int(), float()complex() 将回退为 __index__()
# 示例代码
class Dog:
    def __init__(self, name):
        self.name = name

    def __abs__(self):
        # 调用`abs`时会触发
        print("__abs__被调用了")
        return '1'

    def __bool__(self):
        # 调用`bool`时会触发
        print("__bool__被调用了")
        return False

    def __complex__(self):
        # 调用`complex`时会触发
        print("__complex__被调用了")
        return complex(1)

    def __int__(self):
        # 调用`int`时会触发
        print("__int__被调用了")
        return 1

    def __float__(self):
        # 调用`float`时会触发
        print("__float__被调用了")
        return 1.0

    def __index__(self):
        # 调用`operator.index`时会触发
        print("__index__被调用了")
        return 1

    def __hash__(self):
        # 调用`hash`时会触发
        print("__hash__被调用了")
        return 1


if __name__ == "__main__":
    dg = Dog("bob")
    print("-"*50)
    abs(dg)
    print("-" * 50)
    bool(dg)
    print("-" * 50)
    complex(dg)
    print("-" * 50)
    int(dg)
    print("-" * 50)
    float(dg)
    print("-" * 50)
    import operator
    operator.index(dg)
    print("-" * 50)
    hash(dg)

# 示例代码
class Dog:
    def __init__(self, name):
        self.name = name

    def __abs__(self):
        # 调用`abs`时会触发
        print("__abs__被调用了")
        return '1'

    def __bool__(self):
        # 调用`bool`时会触发
        print("__bool__被调用了")
        return False

    def __complex__(self):
        # 调用`complex`时会触发
        print("__complex__被调用了")
        return complex(1)

    def __int__(self):
        # 调用`int`时会触发
        print("__int__被调用了")
        return 1

    def __float__(self):
        # 调用`float`时会触发
        print("__float__被调用了")
        return 1.0

    def __index__(self):
        # 调用`operator.index`时会触发
        print("__index__被调用了")
        return 1

    def __hash__(self):
        # 调用`hash`时会触发
        print("__hash__被调用了")
        return 1


if __name__ == "__main__":
    dg = Dog("bob")
    print("-"*50)
    abs(dg)
    print("-" * 50)
    bool(dg)
    print("-" * 50)
    complex(dg)
    print("-" * 50)
    int(dg)
    print("-" * 50)
    float(dg)
    print("-" * 50)
    import operator
    operator.index(dg)
    print("-" * 50)
    hash(dg)
# 输出结果
--------------------------------------------------
__abs__被调用了
--------------------------------------------------
__bool__被调用了
--------------------------------------------------
__complex__被调用了
--------------------------------------------------
__int__被调用了
--------------------------------------------------
__float__被调用了
--------------------------------------------------
__index__被调用了
--------------------------------------------------
__hash__被调用了

# 输出结果
--------------------------------------------------
__abs__被调用了
--------------------------------------------------
__bool__被调用了
--------------------------------------------------
__complex__被调用了
--------------------------------------------------
__int__被调用了
--------------------------------------------------
__float__被调用了
--------------------------------------------------
__index__被调用了
--------------------------------------------------
__hash__被调用了

5.迭代枚举

  • __iter__调用此方法以实现内置函数 iter()。此方法在需要为容器创建迭代器时被调用,该返回一个新的迭代器对象,它能够逐个迭代容器中的所有对象。对于映射,它应该逐个迭代容器中的键。
    迭代器对象也需要实现此方法,它们需要返回对象自身。
  • __reversed__此方法会被 reversed() 内置函数调用以实现逆向迭代。它应当返回一个新的以逆序逐个迭代容器内所有对象的迭代器对象。
    如果未提供 __reversed__() 方法,则 reversed()内置函数将回退到使用序列协议 (__len__()__getitem__()。支持序列协议的对象应当仅在能够提供比 reversed() 所提供的实现更高效的实现时才提供 __reversed__() 方法。
  • __next__调用此方法以实现内置函数 next()。可以通过next函数访问这个对象的下一个元素了,并且在你不想继续有迭代的情况下抛出一个StopIteration的异常。

iterableiterator区别

可迭代的(iterable)

如果对象的 __iter__ 方法能返回迭代器,这就是可迭代的对象。序列都是可迭代的对象;此外,实现 __getitem__ 方法的对象也是可迭代的对象。

迭代器(iterator)

实现了无参数方法 __next__的对象;这个方法返回级数里的下一个元素,如果没有元素了就抛出 StopIteration 异常。在 Python 中,迭代器还实现了 __iter__ 方法,因此迭代器也是可迭代的对象。根据最初的设计模式,经典迭代器返回集合里的元素。生成器也是迭代器,不过更灵活。

标准的迭代器接口有两个方法:

__next__ 返回下一个可用的元素,如果没有元素了,抛出 StopIteration异常。__iter__ 返回 self,以便在应该使用可迭代对象的地方使用迭代器,例如在 for 循环中。

# 示例代码
class Person:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __iter__(self):
        # 调用`iter`或者`for`循环遍历时会触发
        print("__iter__被调用了")
        return (x for x in(self.name, self.age, self.sex))

    def __next__(self):
        # 调用`next`时会触发
        print("__next__被调用了")
        return self.age

    def __reversed__(self):
        # 调用`reversed`时会触发
        print("__reversed__被调用了")
        return None


if __name__ == "__main__":
    p = Person("张三", 18, '男')
    print("-"*50)
    iter(p)
    print("-" * 50)
    for i in p:
        print(i)
    print("-" * 50)
    print(next(p))
    print("-" * 50)
    reversed(p)

# 示例代码
class Person:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __iter__(self):
        # 调用`iter`或者`for`循环遍历时会触发
        print("__iter__被调用了")
        return (x for x in(self.name, self.age, self.sex))

    def __next__(self):
        # 调用`next`时会触发
        print("__next__被调用了")
        return self.age

    def __reversed__(self):
        # 调用`reversed`时会触发
        print("__reversed__被调用了")
        return None


if __name__ == "__main__":
    p = Person("张三", 18, '男')
    print("-"*50)
    iter(p)
    print("-" * 50)
    for i in p:
        print(i)
    print("-" * 50)
    print(next(p))
    print("-" * 50)
    reversed(p)
# 输出结果
--------------------------------------------------
__iter__被调用了
--------------------------------------------------
__iter__被调用了
张三
18
男
--------------------------------------------------
__next__被调用了
18
--------------------------------------------------
__reversed__被调用了

# 输出结果
--------------------------------------------------
__iter__被调用了
--------------------------------------------------
__iter__被调用了
张三
18
男
--------------------------------------------------
__next__被调用了
18
--------------------------------------------------
__reversed__被调用了

6.可调用模拟

  • __call__如果类实现了这个方法,相当于把这个类型的对象当作函数来使用,相当于重载了括号运算符。__call__方法把一个对象变成为可调用对象,即通过obj()直接调用对象时,系统执行的是对象的__call__方法。实现__call__方法的对象,callable(obj)返回为True
    应用方面,可以通过__call__语法糖,简化对象的调用;也可以用户实现__call__方法的对象替代基于函数的装饰器,简化代码结构。
# 示例代码
class Cat:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print( self.name + " miao~ maio~ miao~")

    def __call__(self, *args, **kwargs):
        print("__call__被调用了")
        self.speak()


if __name__ == "__main__":
    tom = Cat("Tom")
    print(callable(tom))
    print("-"*50)
    tom()

# 示例代码
class Cat:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print( self.name + " miao~ maio~ miao~")

    def __call__(self, *args, **kwargs):
        print("__call__被调用了")
        self.speak()


if __name__ == "__main__":
    tom = Cat("Tom")
    print(callable(tom))
    print("-"*50)
    tom()
# 输出结果
True
--------------------------------------------------
__call__被调用了
Tom miao~ maio~ miao~

# 输出结果
True
--------------------------------------------------
__call__被调用了
Tom miao~ maio~ miao~

7.上下文管理

  • __enter__进入与此对象相关的运行时上下文。with语句将会绑定这个方法的返回值到 as 子句中指定的目标,如果有的话。
  • __exit__退出关联到此对象的运行时上下文。各个参数描述了导致上下文退出的异常。如果上下文是无异常地退出的,三个参数都将为 None
    如果提供了异常,并且希望方法屏蔽此异常(即避免其被传播),则应当返回真值。否则的话,异常将在退出此方法时按正常流程处理。

__enter____exit__可以让对象通过with关键字来进行使用,提供进入with块前的初始化工作和退出with块后的清理工作,常用于文件和数据库操作中。

使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

# 示例代码
class OpenFile:
    def __init__(self, filepath):
        self.filepath = filepath

    def __enter__(self):
        print('__enter__被调用了')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__被调用了')
        return True # 返回True,可以屏蔽抛出的异常

    def __str__(self):
        return self.filepath


if __name__ == "__main__":
    with OpenFile('a.txt') as f:
        print(f)
        f.write() # 没有此方法,会抛出异常

# 示例代码
class OpenFile:
    def __init__(self, filepath):
        self.filepath = filepath

    def __enter__(self):
        print('__enter__被调用了')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__被调用了')
        return True # 返回True,可以屏蔽抛出的异常

    def __str__(self):
        return self.filepath


if __name__ == "__main__":
    with OpenFile('a.txt') as f:
        print(f)
        f.write() # 没有此方法,会抛出异常
# 输出结果
__enter__被调用了
a.txt
__exit__被调用了

# 输出结果
__enter__被调用了
a.txt
__exit__被调用了

8.属性管理

  • __getattr__当访问对象中不存在的属性时,因引发 AttributeError 异常而失败,系统会去调用对象的__getattr__方法 。此方法应当返回找到的属性值或是引发一个 AttributeError 异常。
    如果属性是通过正常机制找到的,__getattr__() 就不会被调用。

注意:

在实现了__getattr__方法的类的内部,对对象属性进行赋值时需要特别注意,很容易引发__getattr__的无限循环。在__init__中,对象的所有自定义属性都没有初始化,此时如果对属性赋值,会触发调用__setattr__方法,访问属性则触发__getattr__。所以在类内部,最好通过self.__dict__对对象属性进行赋值和取值,避免通过点.运算符(self.attrself.attr = xxx)引发无限循环。

  • __getattribute__一个属性访问拦截器,会拦截所有对对象属性访问请求,不管属性是否存在,且具有最优先的访问查询顺序。
    此方法会无条件地被调用以实现对类实例属性的访问。如果类还定义了 __getattr__(),则后者不会被调用,除非 __getattribute__() 显式地调用它或是引发了 AttributeError异常。此方法应当返回找到的属性值或是引发一个 AttributeError 异常。

注意:

__getattr__示例中,我们通过直接访问self.__dict__来规避触发__getattr__方法,但是__getattribute__在访问对象的已有属性时都会触发,即使是self.__dict__也会引发无限循环,所以__getattribute__方法中不能使用self.__dict__,解决办法是调用父类的__getattribute__方法,即在__getattribute__中通过super().__getattribute__(item)来读取对象的属性。

__getattribute__方法看着很强大,但是由于其敏感性,和容易引起无限循环,所以,正常情况下,并不建议覆写此方法。只有当自己明明白白知道需要通过此方法完成什么效果时才使用。

# 示例代码-只定义了__getattr__方法,没有定义__getattribute__方法时
class Cat:

    def __init__(self, name):
        print('__init__被调用了')
        self.name = name

    def __getattr__(self, item):
        print('__getattr__被调用了')
        return 18


if __name__ == '__main__':
    tom = Cat('Tom')
    print(tom.name)
    print(tom.age)

# 示例代码-只定义了__getattr__方法,没有定义__getattribute__方法时
class Cat:

    def __init__(self, name):
        print('__init__被调用了')
        self.name = name

    def __getattr__(self, item):
        print('__getattr__被调用了')
        return 18


if __name__ == '__main__':
    tom = Cat('Tom')
    print(tom.name)
    print(tom.age)
# 输出结果
__init__被调用了
Tom
__getattr__被调用了
18

# 输出结果
__init__被调用了
Tom
__getattr__被调用了
18

当只定义了__getattr__方法,没有定义__getattribute__方法时,调用不存在的属性或者方法,会触发AttributeError,然后由__getattr__方法进行处理

# 示例代码-当同时定义了__getattr__和__getattribute__方法时
class Cat:

    def __init__(self, name):
        print('__init__被调用了')
        self.name = name

    def __getattr__(self, item):
        print('__getattr__被调用了')
        return 18

    def __getattribute__(self, item):
        print('__getattribute__被调用了')
        if item == 'gender':
            raise AttributeError
        return 22


if __name__ == '__main__':
    tom = Cat('Tom')
    print(tom.name)
    print(tom.age)
    print(tom.gender)

# 示例代码-当同时定义了__getattr__和__getattribute__方法时
class Cat:

    def __init__(self, name):
        print('__init__被调用了')
        self.name = name

    def __getattr__(self, item):
        print('__getattr__被调用了')
        return 18

    def __getattribute__(self, item):
        print('__getattribute__被调用了')
        if item == 'gender':
            raise AttributeError
        return 22


if __name__ == '__main__':
    tom = Cat('Tom')
    print(tom.name)
    print(tom.age)
    print(tom.gender)
# 输出结果
__init__被调用了
__getattribute__被调用了
22
__getattribute__被调用了
22
__getattribute__被调用了
__getattr__被调用了
18

# 输出结果
__init__被调用了
__getattribute__被调用了
22
__getattribute__被调用了
22
__getattribute__被调用了
__getattr__被调用了
18

当同时定义了__getattr____getattribute__方法时,调用不存在的属性或者方法,优先调用__getattribute__方法,只有当__getattribute__方法抛出AttributeError异常时,才会调用__getattr__方法

对象的属性查找顺序如下:实例的__getattribute__ → 实例对象字典 → 实例所在类字典 → 实例所在类的父类(MRO顺序)字典 → 实例所在类的__getattr__ → 报错

  • __setattr__此方法在一个属性被尝试赋值时被调用。这个调用会取代正常机制,即将值保存到实例字典。
  • __delattr__类似于 __setattr__() ,但其作用为删除而非赋值。此方法应该仅在 del obj.name 对于该对象有意义时才被实现。
  • __dir__此方法会在对相应对象调用 dir() 时被调用,返回值必须为一个序列。dir() 会把返回的序列转换为列表并对其排序。

9.属性描述符

  • __get__调用此方法以获取所有者类的属性(类属性访问)或该类的实例的属性(实例属性访问)。可选的 owner 参数是所有者类而 instance 是被用来访问属性的实例,如果通过 owner 来访问属性则返回 None
    此方法应当返回计算得到的属性值或是引发 AttributeError 异常。
  • __set__调用此方法以设置 instance指定的所有者类的实例的属性为新值 value
  • __delete__调用此方法以删除 instance 指定的所有者类的实例的属性。

一个类只要实现了__get____set____delete__中任意一个方法,我们就可以叫它描述器(descriptor)。如果只定义了__get__我们叫非资料描述器(non-data descriptor),如果__set____delete__任意一个/或者同时出现,我们叫资料描述器(data descriptor)。

# 示例代码
class NonDataDescriptor:
    def __get__(self, instance, owner):
        print(instance, owner)
        return '__get__方法被调用了'


class DataDescriptor:
    def __get__(self, instance, owner):
        print(instance, owner)
        return '__get__方法被调用了'

    def __set__(self, instance, value):
        print(instance, value)
        return '__set__方法被调用了'


class Test:
    non_data_des = NonDataDescriptor()
    data_des = DataDescriptor()
    
if __name__ == '__main__':
    t = Test()
    print("测试非资料描述器"+"-"*50)
    print(t.non_data_des)
    print(Test.non_data_des)
    print("通过实例对描述器进行赋值"+"-"*50)
    
    t.non_data_des = 1
    print(t.non_data_des)
    print(Test.non_data_des)

    print("通过类对描述器进行赋值"+"-"*50)
    Test.non_data_des = 1
    print(t.non_data_des)
    print(Test.non_data_des)
    print("测试非资料描述器完成"+"-"*50)

    print("测试资料描述器"+"-"*50)
    print(t.data_des)
    print(Test.data_des)
    print("通过实例对描述器进行赋值"+"-"*50)
    
    t.data_des = 1
    print(t.data_des)
    print(Test.data_des)

    print("通过类对描述器进行赋值"+"-"*50)
    Test.data_des = 1
    print(t.data_des)
    print(Test.data_des)
    print("测试资料描述器完成"+"-"*50)

# 示例代码
class NonDataDescriptor:
    def __get__(self, instance, owner):
        print(instance, owner)
        return '__get__方法被调用了'


class DataDescriptor:
    def __get__(self, instance, owner):
        print(instance, owner)
        return '__get__方法被调用了'

    def __set__(self, instance, value):
        print(instance, value)
        return '__set__方法被调用了'


class Test:
    non_data_des = NonDataDescriptor()
    data_des = DataDescriptor()
    
if __name__ == '__main__':
    t = Test()
    print("测试非资料描述器"+"-"*50)
    print(t.non_data_des)
    print(Test.non_data_des)
    print("通过实例对描述器进行赋值"+"-"*50)
    
    t.non_data_des = 1
    print(t.non_data_des)
    print(Test.non_data_des)

    print("通过类对描述器进行赋值"+"-"*50)
    Test.non_data_des = 1
    print(t.non_data_des)
    print(Test.non_data_des)
    print("测试非资料描述器完成"+"-"*50)

    print("测试资料描述器"+"-"*50)
    print(t.data_des)
    print(Test.data_des)
    print("通过实例对描述器进行赋值"+"-"*50)
    
    t.data_des = 1
    print(t.data_des)
    print(Test.data_des)

    print("通过类对描述器进行赋值"+"-"*50)
    Test.data_des = 1
    print(t.data_des)
    print(Test.data_des)
    print("测试资料描述器完成"+"-"*50)
# 测试结果
测试非资料描述器--------------------------------------------------
<__main__.test>0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过实例对描述器进行赋值--------------------------------------------------
1None <class '__main__.Test'>__get__方法被调用了
通过类对描述器进行赋值--------------------------------------------------
1
1
测试非资料描述器完成--------------------------------------------------
测试资料描述器--------------------------------------------------
<__main__.Test object at 0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过实例对描述器进行赋值--------------------------------------------------
<__main__.Test object at 0x00000198E0DE2610> 1
<__main__.Test object at 0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过类对描述器进行赋值--------------------------------------------------
1
1
测试资料描述器完成--------------------------------------------------
# 测试结果
测试非资料描述器--------------------------------------------------
<__main__.test>0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过实例对描述器进行赋值--------------------------------------------------
1None <class '__main__.Test'>__get__方法被调用了
通过类对描述器进行赋值--------------------------------------------------
1
1
测试非资料描述器完成--------------------------------------------------
测试资料描述器--------------------------------------------------
<__main__.Test object at 0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过实例对描述器进行赋值--------------------------------------------------
<__main__.Test object at 0x00000198E0DE2610> 1
<__main__.Test object at 0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过类对描述器进行赋值--------------------------------------------------
1
1
测试资料描述器完成--------------------------------------------------

描述器的调用是因为__getattribute__,重写 __getattribute__方法会阻止正常的描述器调用,资料描述器总是比实例字典优先,而非资料描述器可能被实例字典重写(非资料描述器不如实例字典优先)。

10.跟类相关的服务

  • __prepare__命名空间钩子
    如果元类具有 __prepare__ 属性,它会以 namespace = metaclass.__prepare__(name, bases, **kwds) 的形式被调用(其中如果有任何额外的关键字参数,则应当来自类定义)。__prepare__ 方法应该被实现为 classmethod()__prepare__ 所返回的命名空间会被传入 __new__,但是当最终的类对象被创建时,该命名空间会被拷贝到一个新的 dict 中。
    如果元类没有 __prepare__ 属性,则类命名空间将初始化为一个空的有序映射。
  • __instancecheck__如果 instance 应被视为 class 的一个(直接或间接)实例则返回真值。如果定义了此方法,则会被调用以实现 isinstance(instance, class)
  • __subclasscheck__Return true 如果 subclass 应被视为 class 的一个(直接或间接)子类则返回真值。如果定义了此方法,则会被调用以实现 issubclass(subclass, class)
    通过 __instancecheck__()__subclasscheck__() 来定制 isinstance()issubclass() 行为,加入此功能的动机是出于向该语言添加抽象基类的内容

11.一元运算符

  • __neg__ -
  • __pos__ +
  • __abs__abs()
  • __invert__ ~
    调用此方法以实现一元算术运算 (-, +, abs()~)。

12.众多比较运算符

  • __lt__ <
  • __le__ <=
  • __eq__ ==
  • __ne__ !=
  • __gt__ >
  • __ge__ >=
    以上这些被称为“富比较”方法。运算符号与方法名称的对应关系如下:x 调用 x.__lt__(y)、x<=y 调用 x.__le__(y)、x==y 调用 x.__eq__(y)、x!=y 调用 x.__ne__(y)、x>y 调用 x.__gt__(y)、x>=y 调用 x.__ge__(y)。如果指定的参数对没有相应的实现,富比较方法可能会返回单例对象 NotImplemented。按照惯例,成功的比较会返回 FalseTrue。不过实际上这些方法可以返回任意值,因此如果比较运算符是要用于布尔值判断(例如作为 if 语句的条件),Python 会对返回值调用 bool() 以确定结果为真还是假。
    在默认情况下,object 通过使用 is 来实现 __eq__(),并在比较结果为假值时返回 NotImplemented: True if x is y else NotImplemented。对于 __ne__(),默认会委托给 __eq__()并对结果取反,除非结果为 NotImplemented。比较运算符之间没有其他隐含关系或默认实现;例如,(x 为真并不意味着 x<=y。

13.算术运算符

  • __add__ +
  • __sub__ -
  • __mul__ *
  • __truediv__ /
  • __floordiv__ //
  • __mod__ %
  • __divmod__divmod()
  • __pow__ ** 或 pow()
  • __round__ round()
    调用这些方法来实现二进制算术运算 (+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |)。例如,求表达式 x + y 的值,其中 x 是具有 __add__()方法的类的一个实例,则会调用 x.__add__(y)__divmod__()方法应该等价于使用 __floordiv__()__mod__(),它不应该被关联到 __truediv__()。请注意如果要支持三元版本的内置 pow() 函数,则 __pow__() 的定义应该接受可选的第三个参数。
    如果这些方法中的某一个不支持与所提供参数进行运算,它应该返回 NotImplemented

14.反向算术运算符

  • __radd__ +
  • __rsub__ -
  • __rmul__ *
  • __rtruediv__ /
  • __rfloordiv__ //
  • __rmod__  %
  • __rdivmod__ divmod()
  • __rpow__ pow()
    调用这些方法来实现具有反射(交换)操作数的二进制算术运算 (+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |)。这些成员函数仅会在左操作数不支持相应运算且两个操作数类型不同时被调用。例如,求表达式 x - y 的值,其中 y 是具有 __rsub__() 方法的类的一个实例,则当 x.__sub__(y) 返回 NotImplemented 时会调用 y.__rsub__(x)

15.增量赋值算术运算符

  • __iadd__ +=
  • __isub__ -=
  • __imul__ *=
  • __itruediv__ /=
  • __ifloordiv__ //=
  • __imod__ %=
  • __ipow__ **=
    调用这些方法来实现扩展算术赋值 (+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=)。这些方法应该尝试进行自身操作 (修改 self) 并返回结果 (结果应该但并非必须为 self)。如果某个方法未被定义,相应的扩展算术赋值将回退到普通方法。例如,如果 x 是具有 __iadd__()方法的类的一个实例,则 x += y 就等价于 x = x.__iadd__(y)。否则就如 x + y 的求值一样选择 x.__add__(y)y.__radd__(x)。在某些情况下,扩展赋值可导致未预期的错误 ,但此行为实际上是数据模型的一个组成部分。

16.位运算符

  • __invert__ ~
  • __lshift__ <<
  • __rshift__ >>
  • __and__ &
  • __or__ |
  • __xor__ ^

调用此方法以实现位运算 (~, <<, >> , &,|,^)。

17.反向位运算符

  • __rlshift__ <<
  • __rrshift__ >>
  • __rand__  &
  • __rxor__ ^
  • __ror__ |

调用此方法以实现反向位运算 ( <<, >> , &,^,|)。

18.增量赋值位运算符

  • __ilshift__ <<=
  • __irshift__ >>=
  • __iand__ &=
  • __ixor__ ^=
  • __ior__ |=

调用此方法以实现反向位运算 (<<=,>>=,&=,^=,|=)。