面对对象进阶
面对对象进阶
1.多继承中的self
class A: def bar(self): print('BAR') self.f1() ''' self本身代表的D类的对象d1 d1如果要在他的父类中去寻找f1 首先应该去C类 ''' class B(A): def f1(self): print('B') class C: def f1(self): print('C') class D(C, B): pass d1 = D() d1.bar()
流程分析:
- d1 = D(),最终找到了object里面的__init__方法创建了对象d1
- d1.bar(),执行bar方法,先去C里找,再去B里面找,最后在A中找到了
- 执行A中的bar方法,遇到self.f1(),此时self依然表示对象d1
- 依然先去C里面找方法f1,最终在C中找到了
2.构造方法
2.1 普通构造方法
class Animal: def __init__(self): print('A构造方法') self.ty = '动物' class Cat(Animal): def __init__(self): print('B构造方法') self.n = '猫' a = Animal() c = Cat() print(a.__dict__) print(c.__dict__) ''' A构造方法 B构造方法 {'ty': '动物'} {'n': '猫'} '''
2.2 如果我们想将父类的字段ty封装子类Cat创建的对象中去呢?
class Animal: def __init__(self): print('A构造方法') self.ty = '动物' class Cat(Animal): def __init__(self): print('B构造方法') self.n = '猫' self.ty = '动物' # 执行父类的构造方法 a = Animal() c = Cat() print(a.__dict__) print(c.__dict__) ''' A构造方法 B构造方法 {'ty': '动物'} {'n': '猫', 'ty': '动物'} '''
2.3 如果父类的字段较多,创建子类的时候想继承这些字段,需要重复的代码量很大
我们可以执行父类的构造方法super
class Animal: def __init__(self): print('A构造方法') self.ty = '动物' self.chi = '吃' self.he = '喝' self.la = '拉' # 方法1 class Cat(Animal): def __init__(self): print('B构造方法') self.n = '猫' super(Cat, self).__init__() # 找到Cat的父类,执行其父类的构造方法 ''' 方法2 class Cat(Animal): def __init__(self): print('B构造方法') self.n = '猫' Animal.__init__(self) ''' a = Animal() c = Cat() print(a.__dict__) print(c.__dict__) ''' A构造方法 B构造方法 A构造方法 {'ty': '动物', 'chi': '吃', 'he': '喝', 'la': '拉'} {'n': '猫', 'ty': '动物', 'chi': '吃', 'he': '喝', 'la': '拉'} '''
3.面对对象中的反射
3.1基本用法
class Foo: def __init__(self, name): # 类的字段 self.name = name def show(self): print('show') obj = Foo('alex') # 通过类,找到类的方法 r1 = hasattr(Foo, 'show') print(r1) ''' True ''' # 通过对象,既可以找到对象的成员,也可以找到类里的成员 r2 = hasattr(obj, 'name') # 找到对象的成员 r3 = hasattr(obj, 'show') # 通过类对象指针,可以找到类的方法 print(r2, r3) ''' True True '''
3.2 通过字符导入模块,用反射操作类的成员
class Foo: def __init__(self, name): # 类的字段 self.name = name def show(self): print('show')
# 通过字符串导入模块 c = __import__('testmoudle', fromlist=True) # 模块名有嵌套的话 # 通过反射获取模块中的类 class_name = getattr(c, 'Foo') # 根据类创建一个对象 obj = class_name('alex') # 通过反射获取name对应的值 val = getattr(obj, 'name') print(val) ''' alex '''
4.类的成员
成员:
字段:普通字段(每个对象都不同的数据),静态字段(每个对象都有一份)
方法:普通方法(使用对象封装的数据),静态方法(无需使用对象封装的内容),类方法(自动将类名传入)
特性:将方法以字段的形式调用
快速判断是由类执行还是对象执行:
有self,对象调用
无self,类调用
class Province: # 静态字段 country = 'China' def __init__(self, name): # 普通字段 self.name = name # self.country = 'china' # 普通方法 def show(self): print('show') # 类方法 @classmethod def xxoo(cls): # cls == class print('xxoo') # 静态方法 @staticmethod def xo(arg1, arg2): print('xo') def start(self): temp = '%s sb' % self.name return temp # 特性,将方法伪造成字段,用于获取 @property def end(self): temp = '%s sb' % self.name return temp @end.setter def end(self, value): print(value) hebei = Province('河北') Province.xo(1, 2) # 方法本需要加括号访问 # @property后 可以以访问字段的方式去访问 obj = Province('alex') ret1 = obj.start() ret2 = obj.end print(ret1, ret2) # 获取字段 # print(obj.name) # 设置字段 # obj.name = '123' # print(obj.name) print(obj.end) # 获取 obj.end = 123 # 设置 ''' ************ 规范 ************** 1.通过类访问的有: 静态字段,静态方法,类方法(静态方法的特殊形式) Province.country Province.xo(1, 2) 2.通过对象访问的有:普通字段,类的方法 hebei.name hebei.show() ************ 规范 ************** 3.静态字段存在的意义 将每个对象都存在的东西只在类里保存一份就行 4.静态方法存在的意义 普通方法执行前需要先创建对象 静态方法不需要先创建对象,类 似于将一个普通的函数放在类中, 在c#和java中非常有用因为他们只能 面向对象,必须要创建类 5.类方法和静态方法一样,只能通过类访问 区别在于 静态方法,参数任意 类方法会自动获取当前类的类名,作为参数传入 '''
5.成员修饰符
对于私有的,只有自己可以访问,其他(包括子类)都不能访问
5.1 对于字段
class Foo: A = 'aa' __A = 'aa' def __init__(self): self.name = 'alex' self.__name = 'wuwen' def aaa(self): print(Foo.__A) print(self.__name) def bbb(self): pass class Bar(Foo): def fetch(self): print(self.__name) # 静态字段 print(Foo.A) # 共有在类外类里均可访问 #print(Foo.__A) obj = Foo() # 私有只能在类里访问 obj.aaa() # 普通字段 print(obj.__name) # 不能在外部访问 obj.aaa() # 可在外部访问 obj1 = Bar() # 执行父类的构造方法 print(obj.__name) # 继承的子类在外部不能访问 obj1.fetch() # 不能访问 obj1.aaa() # 不能访问
5.2 对于方法
class Foo: def __init__(self): pass def __aaa(self): print('aaa') def bbb(self): self.__aaa() @staticmethod def __eee(): print('ddd') @staticmethod def fff(): Foo.__eee() # 普通方法 obj = Foo() #obj.__aaa() # 不能直接访问 obj.bbb() # 间接访问 # 静态方法 # Foo.__eee() # 静态方法在外部通过类无法访问 Foo.fff() # 间接访问私有的静态方法 # 类方法与特性均是一样的道理
5.3 python与其他语言的区别之处,强行访问私有成员
对象._类+私有成员
class Foo: def __init__(self): self.name = 'alex' self.__name = 'wuwen' def aaa(self): print(self.__name) def bbb(self): pass obj = Foo() # obj.__name 不能直接访问 print(obj._Foo__name) # 强行访问 ''' wuwen '''
6.类的特殊成员
__init__
__del__
__call__
__getitem__
__setitem__
__delitem__
__new__
__metaclass__
6.1 对于字典的一些操作,由dict创建的对象为什么可以使用[ ],del等快捷键操作该对象
__init__
__getitem__
__setitem__
__delitem__
# dic = dict(k1=123, k2=456) 因为dic类中有__init__方法 # print(dic['k1']) 为什么对象可以加[]呢 因为dic类中有__getitem__方法 # dic['k1'] = 456 因为dic类中有__setitem__方法 # del dic['k2'] 因为dic类中有__delitem__方法 class Foo: def __init__(self): print('init') def __call__(self, *args, **kwargs): print('call') def __getitem__(self, item): print(item) def __setitem__(self, key, value): print(key, value) def __delitem__(self, key): print('模拟删除成功!!') obj = Foo() obj() # 对象后面加()执行__call__ obj['66'] # 对象后加[]执行__getitem__ obj['77'] = 77 del obj['77'] ''' init call 66 77 77 '''
6.2 对于列表,他的切片操作又是如何在类中实现的呢?
__getitem__
r = list([11, 22, 33, 44, 55, 66, 77]) ret = r[1:6:2] # 切片 print(ret) class Foo: def __getitem__(self, item): print(item, type(item)) obj = Foo() obj() obj[1:3] # 切片的时候,将1:3传入一个slice类得到一个对象再传入getitem ''' slice(1, 3, None) <class 'slice'> '''
6.3 __dict__
class Foo: ''' 我是类的注释 ''' def __init__(self): self.name = 'alex' self.gender = 'female' def __call__(self, *args, **kwargs): print('call') def __getitem__(self, item): print(item, type(item)) def __setitem__(self, key, value): print(key, value) def __delitem__(self, key): print('模拟删除成功!!') obj = Foo() print(obj.__dict__) # 查看对象封装的字段 ''' {'name': 'alex', 'gender': 'female'} ''' print(Foo.__dict__) # 查看类的成员 ''' {'__module__': '__main__', '__doc__': '\n 我是类的注释\n ', '__init__': <function Foo.__init__ at 0x000000000220BA60>, '__call__': <function Foo.__call__ at 0x000000000220BAE8>, '__getitem__': <function Foo.__getitem__ at 0x000000000220BB70>, '__setitem__': <function Foo.__setitem__ at 0x000000000220BBF8>, '__delitem__': <function Foo.__delitem__ at 0x000000000220BC80>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>} '''
6.4 类似于字符串,列表,为什么我们可以用for循环这个对象呢?
事实上:如果for循环对象的时候,默认执行类中的__iter__方法,是一个生成器
所以一个对象可以被for循环的话,说明这个对象的类中有__iter__ 方法
li = [1, 2, 3] # 等价于 li = list([]) for i in li: print(i) ''' 1 2 3 ''' # 实现一个简单版本的list类 class Foo: def __iter__(self): yield 1 yield 2 yield 3 li = Foo() for i in li: print(i) ''' 1 2 3 '''
6.5 __new__和__metaclass__