面向对象高级(待持续补充)
# 递归现象的产生
# -通过反射赋值 setattr(obj,key,value)
# -self.key = value
# 避免方法
# (1)self.__dict__[key] = value
# (2)object.__setattr__(self,key,value)
# class Foo:
# def __init__(self, name):
# self.name = name # 在这里会触发__setattr__的执行,所以如果重写了__setattr__,那么就会产生递归
#
# def __setattr__(self, key, value):
# if key == 'age':
# if isinstance(value, int):
# # 往对象中放属性,反射
# # setattr(self,key,value) # setattr本质还是调用了self.key=value,会发生递归
# # self.age = value # 也会发生递归
# # 避免递归问题:
# # (1)
# # self.__dict__[key] = value
# # (2)普通函数,有几个参数,就传几个参数
# object.__setattr__(self, key, value)
# else:
# raise Exception('不让放')
# else:
# object.__setattr__(self, key, value)
#
# def printsize(self):
# print(self.__size)
#
# def __getattr__(self, item): # 重写__getattr__
# print(item) # item 就是 key
# return 99 # 如果赋予了返回值,那么调用已有属性得到的结果是对应属性值,如果调用了对象中没有的属性,就会得到该返回值
#
#
# f = Foo('aaa')
# print(f.__dict__)
# print(f.name)
# f.age = 19 # 触发__setattr__的执行
# print(f.age) # 触发__getattr__的执行
# object.__setattr__(f, '_Foo__size', 20)
# # print(f.size) # 对象中没有属性,会报错
# print(f._Foo__size)
# print(f.xxx)
# # 在不继承字典的情况下,支持通过[]来赋值和取值
# class Bar:
# def __setitem__(self, key, value):
# setattr(self,key,value)
# # 或 object.__setattr__(self, key, value)
#
# def __getitem__(self, item):
# return getattr(self, item)
#
# b = Bar()
# b.name = 'bbb'
# print(b.name)
# b['age'] = 20
# # b.age = 20
# print(b.age)
# __str__
# class Student:
# def __init__(self, name, age):
# self.name = name
# self.age = age
#
# # 定义对象的字符串表示
# def __str__(self):
# return self.name
#
#
# s1 = Student('张三', 29)
# print(s1.name)
# print(s1.age)
# print(s1) # 会调用s1的__str__方法,若没有重写__str__则输出对象的地址
#
# __repr__
# 在python解释器环境下,会默认显示对象的repr表示。
# >>> class Student:
# ... def __init__(self, name, age):
# ... self.name = name
# ... self.age = age
# ... def __repr__(self):
# ... return self.name
# ...
# >>> s1 = Student('张三', 24)
# >>> s1
# 总结:
# str函数或者print函数调用的是obj.__str__(),repr函数或者交互式解释器调用的是obj.__repr__()
# 注意:
# 如果__str__没有被定义,那么就会使用__repr__来代替输出。
# __str__和__repr__方法的返回值都必须是字符串。
# __slots__
# 当一个类设置了__slots__属性后,这个类的__dict__属性就不存在了(同理,该类的实例也不存在__dict__属性),如此一来,设置了__slots__属性的类的属性,只能是预先设定好的。
# 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的小型数组来构建的,而不是为每个实例都定义一个__dict__字典,在__slots__中列出的属性名在内部被映射到这个数组的特定索引上。使用__slots__带来的副作用是我们没有办法给实例添加任何新的属性了。
# 注意:
# 尽管__slots__看起来是个非常有用的特性,但是除非你十分确切的知道要使用它,否则尽量不要使用它。比如定义了__slots__属性的类就不支持多继承。__slots__通常都是作为一种优化工具来使用。
#
# class A:
# __slots__ = ['name', 'age'] # 对象中只能拥有name和age属性
#
#
# a1 = A()
# # print(a1.__dict__) # AttributeError: 'A' object has no attribute '__dict__'
# a1.name = '张三'
# a1.age = 24
# # a1.hobby = 'play' # AttributeError: 'A' object has no attribute 'hobby'
# print(a1.__slots__)
# __call__
# class Person:
# def __init__(self, name, age):
# self.name = name
# self.age = age
#
# def __call__(self, *args, **kwargs):
# print('调用对象的__call__方法')
#
#
# a = Person('张三', 24) # Person也是对象,加括号触发元类的__call__
# a() # __call__方法使对象加括号可被调用
# __doc__
# 定义类的描述信息。注意该信息无法被继承。
# class A:
# """我是A类的描述信息"""
# pass
#
# print(A.__doc__)
# __eq__ 拥有__eq__方法的对象支持相等的比较操作
# class A:
# def __init__(self,x,y):
# self.x = x
# self.y = y
#
# def __eq__(self,obj): # a=>self,b=>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(2,1)
# print(a == b)
# type 和 object 的关系
# type类,继承object,是所有类的类,包括自己
# object类:谁都没继承,它是由type实例化得到的,是所有类的父类,包括type(但不包括object)