首页 > 代码库 > Python面向对象-类成员

Python面向对象-类成员

类的成员可以分为三大类:字段、方法和属性:

技术分享

注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。

 

(一)字段

字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同

 1 class Province:
 2     # 静态字段
 3     country = "China"
 4 
 5     def __init__(self, name):
 6         # 普通字段
 7         self.name = name
 8 
 9 obj_henan = Province("HeNan")
10 
11 print(obj_hunan.name) # 使用对象访问跑一趟那个字段
12 print(Province.country) # 使用类访问静态字段

由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。其在内容的存储方式类似如下图:

技术分享

由上图可是:

  • 静态字段在内存中只保存一份
  • 普通字段在每个对象中都要保存一份

应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

 

(二)方法

方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。

  • 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self
  • 类方法:由调用; 至少一个cls参数;执行类方法时,自动将调用该方法的复制给cls
  • 静态方法:由调用;无默认参数;
 1 class Foo:
 2     def __init__(self, name):
 3         self.name = name
 4 
 5     # 普通方法,由对象调用
 6     def ord_fun(self):
 7         print(self.name)
 8 
 9     # 静态方法
10     # 普通方法经过两步变成静态字段,1,去掉参数self,2,加上装饰器 @staticmethod
11     # 如果类里的方法用不到对象(self)里的字段,静态方法可以有除了self的参数
12     # 静态方法其实就是面向过程里的普通函数,之所以放到类里就是表示方法和该类有关
13     @staticmethod
14     def static_func(arg1):
15         print(arg1)
16 
17     # 类方法
18     # 静态方法的一种特殊形式,自带特殊参数cls,使用装饰器 @classmethod
19     @classmethod
20     def class_func(cls): # cls = class
21         print(cls)
22 
23 obj_foo = Foo("foo object")
24 obj_foo.ord_fun()
25 Foo.static_func("static func")
26 Foo.class_func()

 

技术分享

相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。

不同点:方法调用者不同、调用方法时自动传入的参数不同。

 

(三) 属性

为了理解Python的属性,我们先来看一个例子,

假如我们有一个学生类:

1 class Student(object):
2     def __init__(self):
3         self.score = 0
4 
5 s1 = Student()
6 s1.score = 9999

这个赋值 s1.score = 9999 这显然不合逻辑,score的正常取值范围应该是0-100, 为了限制score的范围,我们可能会通过定义一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩,这样,在set_score()方法里,就可以检查参数:

 1 class Student(object):
 2     def __init__(self):
 3         pass 4 
 5     def set_score(self, value):
 6         if not isinstance(value, int):
 7             raise ValueError("Score must be int type")
 8         elif score < 0 or value> 100:
 9             raise ValueError("Score must between 0 - 100")
10         else:
11             self.score = value
12 
13 s1 = Student()
14 s1.set_score(9999)

这时 s1.set_score(9999) 就会有如下的报错:

1     raise ValueError("Score must between 0 - 100")
2 ValueError: Score must between 0 - 100

但是,上面的调用方法又略显复杂

有没有既能检查参数,又可以类似于访问字段一样设置score的方法呢?(使用score = 9999,而不用s1.set_score(9999)), 对于追求完美的Python程序员来说,这是必须要做到的!

还记得装饰器(decorator)可以给函数动态加上功能吗?对于类的方法,装饰器一样起作用。Python内置的@property装饰器就是负责把一个方法变成属性调用的:

于是我们对上面的Studeng类做修改:

 1 class Student(object):
 2     def __init__(self):
 3         pass
 4 
 5     @property
 6     def score(self, score):
 7         return self.score
 8 
 9     @score.setter
10     def score(self, value): # 函数名和@property一致
11         if not isinstance(value, int):
12             raise ValueError("Score must be int type")
13         elif value < 0 or value > 100:
14             raise ValueError("Score must between 0 - 100")
15         else:
16             self.score = value
17 
18 s1 = Student()
19 s1.score = 9999 # 这里用 "="访问score

@property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:

1 raise ValueError("Score must between 0 - 100")
2 ValueError: Score must between 0 - 100

注意到这个神奇的@property,我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。

还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:

 1     @property
 2     def birth(self):
 3         return self._birth
 4 
 5     @birth.setter
 6     def birth(self, value):
 7         self._birth = value
 8 
 9     @property
10     def age(self):
11         return 2017 - self._birth

上面的birth是可读写属性,而age就是一个只读属性,因为age可以根据birth和当前时间计算出来。

@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

Python面向对象-类成员