复习----面向对象(进阶)
Posted yan-night
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了复习----面向对象(进阶)相关的知识,希望对你有一定的参考价值。
本节内容:
1、类的成员
1.1字段
1.2方法
1.3属性
2、成员修饰符
3、类的特殊成员
1、类的成员
类的成员可以分为三大类:字段、方法、属性。
字段分为:普通字段、静态字段。
方法分为:普通方法、静态方法、类方法。
所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
1.1字段
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同。
- 普通字段属于对象,即将字段存于对象内。
- 静态字段属于类,即将字段存于类内。
语法如下
1 class Province: 2 # 静态字段,属于类 3 country = ‘中国‘ 4 5 def __init__(self, name): 6 # 普通字段,属于对象 7 self.name = name 8 9 #创建对象 10 henan = Province(‘河南‘) 11 hebei = Province(‘河北‘) 12 13 #直接访问静态字段 14 print(Province.country) #输出:中国 15 16 #直接访问普通字段 17 print(henan.name) #输出:河南
由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。
其在内容的存储方式类似如下图:
由上图可示:
- 静态字段在内存中只保存一份
- 普通字段在每个对象中都要保存一份
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段。
1.2方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
-
普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self。
-
类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls。
-
静态方法:由类调用;无默认参数。
普通方法
1 class Foo: 2 3 #普通方法 4 def func(self): 5 #定义普通方法,至少有一个self参数 6 print(‘do something...‘) 7 8 #调用普通方法 9 obj_1 = Foo() 10 obj_1.func()
类方法
1 class Foo: 2 3 #类方法 4 @classmethod 5 def func(cls): 6 #定义类方法,至少有一个cls参数 7 print(‘do something...‘) 8 9 #调用类方法,cls是类名 10 Foo.func()
静态方法
1 class Foo: 2 3 #静态方法 4 @staticmethod 5 def func(): 6 #定义静态方法,无默认参数 7 print(‘do something...‘) 8 9 #调用静态方法,不用通过对象调用 10 Foo.func()
- 相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
- 不同点:方法调用者不同、调用方法时自动传入的参数不同。
1.3属性
Python中的属性其实是普通方法的变种。
属性方法的作用就是通过@property把一个方法变成一个静态属性。
语法
1 class Foo: 2 3 #普通方法 4 def func(self): 5 #定义普通方法,至少有一个self参数 6 print(‘do something...‘) 7 8 #定义属性 9 @property 10 def pro_func(self): 11 print(‘do something...‘) 12 13 #创建对象 14 obj_1 = Foo() 15 16 #调用普通方法 17 obj_1.func() 18 19 #调用属性,调用时不用括号,用则报错 20 obj_1.pro_func
由属性的定义和调用要注意一下几点:
- 定义时,在普通方法的基础上添加 @property 装饰器;
- 定义时,属性仅有一个self参数
- 调用时,无需括号
属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象。
属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。
属性的其他操作
1 class Foo: 2 3 @property 4 def pro_func(self): 5 print(‘do something...‘) 6 7 @pro_func.setter 8 def pro_func(self,value): 9 print(‘do something...‘) 10 11 @pro_func.deleter 12 def pro_func(self): 13 print(‘do something...‘) 14 15 #调用 16 obj = Foo() 17 18 obj.pro_func #自动执行 @property 修饰的 pro_func 方法,并获取方法的返回值 19 20 obj.pro_func = 666 #自动执行 @pro_func.setter 修饰的 pro_func 方法,并将 666 赋值给方法的参数 21 22 del obj.pro_func #自动执行 @pro_func。deleter 修饰的 pro_func方法 23 24 #调用与方法一一对应,当出现此调用语句时,执行该方法,仅仅是执行该方法。而所执行的程序,由方法内部定义。
2、成员修饰符
对于每一个类的成员而言都有两种形式:
- 公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能访问
私有成员和公有成员的定义不同
私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)
1 class Foo: 2 def __init__(self,name,age): 3 self.name = name 4 #self.age = age #公有字段 5 self.__age = age #私有字段
私有成员和公有成员的访问限制不同:
静态字段
- 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
- 私有静态字段:仅类内部可以访问;
1 class C: 2 3 name = "公有静态字段" 4 5 def func(self): 6 print C.name 7 8 class D(C): 9 10 def show(self): 11 print C.name 12 13 14 C.name # 类访问 15 16 obj = C() 17 obj.func() # 类内部可以访问 18 19 obj_son = D() 20 obj_son.show() # 派生类中可以访问
1 class C: 2 3 __name = "公有静态字段" 4 5 def func(self): 6 print C.__name 7 8 class D(C): 9 10 def show(self): 11 print C.__name 12 13 14 C.__name # 类访问 ==> 错误 15 16 obj = C() 17 obj.func() # 类内部可以访问 ==> 正确 18 19 obj_son = D() 20 obj_son.show() # 派生类中可以访问 ==> 错误
普通字段
- 公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
- 私有普通字段:仅类内部可以访问;
ps:如果想要强制访问私有字段,可以通过 【对象._类名__私有字段明 】访问(如:obj._C__foo),不建议强制访问私有成员。
1 class C: 2 3 def __init__(self): 4 self.foo = "公有字段" 5 6 def func(self): 7 print self.foo # 类内部访问 8 9 class D(C): 10 11 def show(self): 12 print self.foo # 派生类中访问 13 14 obj = C() 15 16 obj.foo # 通过对象访问 17 obj.func() # 类内部访问 18 19 obj_son = D(); 20 obj_son.show() # 派生类中访问
1 class C: 2 3 def __init__(self): 4 self.__foo = "私有字段" 5 6 def func(self): 7 print self.foo # 类内部访问 8 9 class D(C): 10 11 def show(self): 12 print self.foo # 派生类中访问 13 14 obj = C() 15 16 obj.__foo # 通过对象访问 ==> 错误 17 obj.func() # 类内部访问 ==> 正确 18 19 obj_son = D(); 20 obj_son.show() # 派生类中访问 ==> 错误
方法、属性的访问于上述方式相似,即:私有成员只能在类内部使用
ps:非要访问私有属性的话,可以通过 对象._类__属性名
3、类的特殊成员
3.1 __doc__
表示类的描述信息
1 class Foo: 2 """类的信息""" 3 4 def fnce(self): 5 pass 6 7 print(Foo.__doc__) #输出:类的信息
3.2 __module__和__class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
1 class Foo: 2 """类的信息""" 3 def __init__(self): 4 self.name="zhangsan" 5 6 def fnce(self): 7 pass 8 9 # print(Foo.__doc__) #输出:类的信息 10 11 obj = Foo() 12 print(obj.__module__) #输出:__main__,即输出模块 13 print(obj.__class__) #输出:<class ‘__main__.Foo‘>,即输出类
3.3 __init__
构造方法,通过类创建对象时,自动触发
1 class Foo: 2 """类的信息""" 3 def __init__(self,name): 4 self.name = name 5 self.age = 20 6 print(‘执行中...‘) 7 8 def fnce(self): 9 pass 10 11 obj = Foo(‘张三‘) #输出:执行中... 自动执行类中的 __init__ 方法
3.4 __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
3.5 __call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
1 class Foo: 2 """类的信息""" 3 def __init__(self,name): 4 self.name = name 5 self.age = 20 6 print(‘执行中...‘) 7 8 def __call__(self): 9 pass 10 11 obj = Foo(‘张三‘) #输出:执行中... 自动执行类中的 __init__ 方法 12 13 obj() # =>Foo()() 执行__call__方法
3.6 __dict__
查看类或对象中的所有成员,将 字段/类 成员通过字典展示。
类的普通字段属于对象;类中的静态字段和方法等属于类。
1 class Province: 2 3 country = ‘China‘ 4 5 def __init__(self, name, count): 6 self.name = name 7 self.count = count 8 9 def func(self, *args, **kwargs): 10 print ‘func‘ 11 12 # 获取类的成员,即:静态字段、方法、 13 print Province.__dict__ 14 # 输出:‘country‘: ‘China‘, ‘__module__‘: ‘__main__‘, ‘func‘: <function func at 0x10be30f50>, ‘__init__‘: <function __init__ at 0x10be30ed8>, ‘__doc__‘: None 15 16 obj1 = Province(‘HeBei‘,10000) 17 print obj1.__dict__ 18 # 获取 对象obj1 的成员 19 # 输出:‘count‘: 10000, ‘name‘: ‘HeBei‘ 20 21 obj2 = Province(‘HeNan‘, 3888) 22 print obj2.__dict__ 23 # 获取 对象obj1 的成员 24 # 输出:‘count‘: 3888, ‘name‘: ‘HeNan‘
3.7 __str__
如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
1 class Foo: 2 3 def __str__(self): 4 return ‘这是__str__方法‘ 5 6 obj = Foo() 7 print(obj) #输出:这是__str__方法
3.8 __getitem__、__setitem__、__delitem
用于索引操作,如字典。以上分别表示获取、设置、删除数据
1 class Foo: 2 3 def __getitem__(self, key): 4 print ‘__getitem__‘,key 5 6 def __setitem__(self, key, value): 7 print ‘__setitem__‘,key,value 8 9 def __delitem__(self, key): 10 print ‘__delitem__‘,key 11 12 13 obj = Foo() 14 15 result = obj[‘k1‘] # 自动触发执行 __getitem__ 16 obj[‘k2‘] = ‘wupeiqi‘ # 自动触发执行 __setitem__ 17 del obj[‘k1‘] # 自动触发执行 __delitem__
执行命令与方法一一对应,出现该命令则执行对于方法。而其作用则由方法内部定义。
3.9 getattr()、hasattr()、setattr()、delattr()
通过字符串的形式操作对象中的成员
- getattr() 函数用于返回一个对象属性值。
- hasattr() 函数用于判断对象是否包含对应的属性。True or False。
- setattr() 函数对应函数 getattr(),用于设置属性值,该属性不一定是存在的。
- delattr 函数用于删除属性。delattr(x, ‘foobar‘) 相等于 del x.foobar。
1 class Foo: 2 name = ‘李四‘ 3 age = 21 4 5 def func(self): 6 return 111 7 8 obj = Foo() 9 10 print(getattr(obj,‘name‘)) #输出:李四 11 print(getattr(obj,‘func‘)) #输出:<bound method Foo.func of <__main__.Foo object at 0x000001DB08168160>> 12 13 r = getattr(obj,‘func‘) 14 print(r()) #输出:111 15 # print(getattr(obj,‘country‘)) #属性 country 不存在,报错! 16 print(getattr(obj,‘country‘,‘China‘)) #属性 country 不存在,但设置了默认值 17 18 print(hasattr(obj,‘name‘)) #True 19 print(hasattr(obj,‘show‘)) #False 20 21 print(getattr(obj,‘age‘)) #21 22 setattr(obj,‘age‘,30) #设置 age 属性,如果属性不存在会创建一个新的对象属性,并对属性赋值. 23 print(getattr(obj,‘age‘)) #30 24 25 print(getattr(obj,‘name‘)) #李四 26 delattr(obj,‘name‘) #删除 name 属性 27 print(getattr(obj,‘name‘)) #触发错误
3.10 __iter__
用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__。
1 #第一步 2 class Foo: 3 pass 4 5 obj = Foo() 6 7 for i in obj: 8 print(i) 9 #报错:TypeError: ‘Foo‘ object is not iterable 10 11 12 #第二步 13 class Foo: 14 15 def __iter__(self): 16 pass 17 18 obj = Foo() 19 20 for i in obj: 21 print(i) 22 # 报错:TypeError: iter() returned non-iterator of type ‘NoneType‘ 23 24 25 #第三步 26 class Foo: 27 28 def __init__(self, sq): 29 self.sq = sq 30 31 def __iter__(self): 32 return iter(self.sq) 33 34 obj = Foo([11,22,33,44]) 35 36 for i in obj: 37 print(i)
以上步骤可以看出,for循环迭代的其实是 iter([11,22,33,44]) ,所以执行流程可以变更为:
1 obj = iter([11,22,33,44]) 2 3 4 for i in obj: 5 print(i)
3.11 __new__ 和 __metaclass__
万物皆对象
1 class Foo(object): 2 3 def __init__(self): 4 pass 5 6 obj = Foo() # obj是通过Foo类实例化的对象
上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。
1 print (type(obj)) # 输出:<class ‘__main__.Foo‘> 表示,obj 对象由Foo类创建 2 print (type(Foo)) # 输出:<type ‘type‘> 表示,Foo类对象由 type 类创建
所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
那么,创建类就可以有两种方式:
普通方法:
1 class Foo(object): 2 3 def func(self): 4 print(‘hello wupeiqi‘)
特殊方法(type类的构造函数):
1 def func(self): 2 print(‘hello wupeiqi‘) 3 4 Foo = type(‘Foo‘,(object,), ‘func‘: func) 5 #type第一个参数:类名 6 #type第二个参数:当前类的基类 7 #type第三个参数:类的成员
==>类 是由 type 类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
1 class MyType(type): 2 3 def __init__(self, what, bases=None, dict=None): 4 super(MyType, self).__init__(what, bases, dict) 5 6 def __call__(self, *args, **kwargs): 7 obj = self.__new__(self, *args, **kwargs) 8 9 self.__init__(obj) 10 11 class Foo(object): 12 13 __metaclass__ = MyType 14 15 def __init__(self, name): 16 self.name = name 17 18 def __new__(cls, *args, **kwargs): 19 return object.__new__(cls, *args, **kwargs) 20 21 # 第一阶段:解释器从上到下执行代码创建Foo类 22 # 第二阶段:通过Foo类创建obj对象 23 obj = Foo()
执行流程:
1、首先创建Foo类时,执行type的__init__方法 => class Foo:
2、当创建对象obj时,执行type的__call__方法 => obj = Foo()
3、然后type的__call__方法指引Foo类执行__new__方法
4、接着由“object”创建出来对象
5、最后由type的__call__方法指引Foo类执行__init__方法
以上是关于复习----面向对象(进阶)的主要内容,如果未能解决你的问题,请参考以下文章