面对对象进阶
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__