复习----面向对象(进阶)

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__方法

技术图片

 

 

 

 

以上是关于复习----面向对象(进阶)的主要内容,如果未能解决你的问题,请参考以下文章

python全栈学习--面向对象进阶3

阿里云名师课堂Java面向对象开发40:引用传递实际应用

python进阶:面向对象进阶

面向对象复习

Python - - 面向对象 - - 面向对象进阶

Python基础-第七天-面向对象编程进阶和Socket编程简介