24-2 面向对象(类的成员、类成员修饰符、类的特殊成员)
类的成员
类的成员可以分为三大类:字段、方法和属性。
我个人理解:“字段”称为属性,“属性”称为属性样的方法更有利于理解。

注:所有成员中,只有普通字段的内容保存在对象中,即:根据此类创建了多少对象,在内存中就有多少份普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
一、字段
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,
- 普通字段 保存在对象中,只能通过对象访问(叫 “实例变量”更好一些)
- 静态字段 保存在类中,可以通过类访问,也可以通过对象访问
class Province: # 静态字段,属于类 country = '中国' def __init__(self, name): # 普通字段,属于对象 self.name = name #直接访问普通字段 henan = Province('河南') print(henan.name) # 河南 henan.name = "河南南" # 将对象的name修改为‘河南南’ #直接访问静态字段 print(Province.country) # 中国 通过类访问 print(henan.country) # 中国 通过对象访问

由上图可知:
- 静态字段在内存中只保存一份
- 普通字段在每个对象中都要保存一份
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段值,那么就使用静态字段。
二、方法
在类中定义的函数,称为方法。
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 静态方法:由类调用;无默认参数;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;
1. 静态方法
通过@staticmethod 装饰器即可把其装饰的方法变为一个静态方法。
普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了。它可以通过类名或实例名被调用。
class Dog(object): def __init__(self,name): self.name = name @staticmethod # 变为静态方法后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了 def eat(self): # 这里的self已经不是自动传入的实例了,仅仅是一个名叫self的形参而已 print("%s is eating" % self.name) d = Dog("dahuang") d.eat() # 报错:TypeError: eat() missing 1 required positional argument: 'self'
想让上面的代码可以正常工作有两种办法:
- 调用时主动传递实例本身给eat方法,即d.eat(d)
- 在eat方法中去掉self参数,但这也意味着,在eat中不能通过self.调用实例中的其它变量了
2. 类方法
类方法通过@classmethod装饰器实现,类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量。
class Dog(object): def __init__(self, name): self.name = name @classmethod # 变为类方法后,再通过实例调用时会自动把类本身当作一个参数传给cls def eat(cls): # cls只是一个变量名,可以写成其他 print("%s is eating" % cls.name) d = Dog("dahuang") d.eat() # 报错:type object 'Dog' has no attribute 'name' 因为name是实例变量,类方法是不能访问实例变量的
让上面的代码可以正常工作:此时可以定义一个类变量,也叫name
class Dog(object): name = "我是类变量" def __init__(self, name): self.name = name @classmethod # 变为类方法后,再通过实例调用时会自动把类本身当作一个参数传给cls def eat(cls): # cls只是一个变量名,可以写成其他 print("%s is eating" % cls.name) d = Dog("dahuang") d.eat() # 输出:我是类变量 is eating
三、属性方法
属性方法的作用就是通过@property把一个方法变成一个静态属性。调用时不需要加(),直接 object.属性方法名 即可,即像调用字段一样调用方法。
1. 属性方法的定义
class Dog(object): def __init__(self,name): self.name = name @property def eat(self): # 参数仅有一个self print(" %s is eating" %self.name) d = Dog("dahuang") # d.eat() # 报错:'NoneType' object is not callable d.eat # 输出:dahuang is eating
2. 属性存在的意义
把一个方法变成静态属性有什么卵用呢?既然想要静态字段,那直接定义成一个静态字段不就得了么?
因为有些值是一系列动作后才得到的结果,每次调用时,它都要经过一系列的动作才得到结果,但这些动作过程不需要用户关心, 用户只需要调用这个属性就可以了。
比如 ,你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了, 想知道这种状态:
class Flight(object): def __init__(self,name): self.flight_name = name def checking_status(self): print("checking flight %s status " % self.flight_name) return 1 @property def flight_status(self): status = self.checking_status() if status == 0 : print("flight got canceled...") elif status == 1 : print("flight is arrived...") elif status == 2: print("flight has departured already...") else: print("cannot confirm the flight status...,please check later") f = Flight("CA980") f.flight_status
3. 属性的修改
以上例子我只能查询航班状态, 既然这个flight_status已经是个属性了, 那我能否给它赋值呢?
f.flight_status = 2 # 报错:can't set attribute
当然可以改, 不过需要通过@flight_status.setter、@flight_status.deleter装饰器再装饰一下,此时 你需要写新方法, 对这个flight_status进行更改。
class Flight(object): def __init__(self,name): self.flight_name = name def checking_status(self): print("checking flight %s status " % self.flight_name) return 1 @property def flight_status(self): status = self.checking_status() if status == 0: print("flight got canceled...") elif status == 1: print("flight is arrived...") elif status == 2: print("flight has departured already...") else: print("cannot confirm the flight status...,please check later") @flight_status.setter # 修改 def flight_status(self,status): status_dic = { 0: "canceled", 1:"arrived", 2: "departured" } print("\033[31;1mHas changed the flight status to \033[0m",status_dic.get(status) ) @flight_status.deleter # 删除 def flight_status(self): print("status got removed...") f = Flight("CA980") f.flight_status f.flight_status = 2 # 触发 @flight_status.setter del f.flight_status # 触发 @flight_status.deleter
4. 属性的两种定义方式
属性的定义有两种方式:
-
- 装饰器 即:在方法上应用装饰器
- 静态字段 即:在类中定义值为property对象的静态字段
(1)装饰器方式
class Goods(object): @property def price(self): print ('@property') @price.setter def price(self, value): print ('@price.setter') @price.deleter def price(self): print ('@price.deleter') obj = Goods() obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值 obj.price = 123 # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数 del obj.price # 自动执行 @price.deleter 修饰的 price 方法
我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除。
class Goods(object): def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deltter def price(self, value): del self.original_price obj = Goods() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 del obj.price # 删除商品原价 实例
(2)静态字段方式
创建值为property对象的静态字段。有个四个参数:
-
- 第一个参数是方法名,调用 “
对象.属性” 时自动触发执行方法 - 第二个参数是方法名,调用 “
对象.属性=XXX”时自动触发执行方法 - 第三个参数是方法名,调用 "
del 对象.属性" 时自动触发执行方法 - 第四个参数是字符串,调用 "
对象.属性.__doc__" ,此参数是该属性的描述信息
- 第一个参数是方法名,调用 “
class Foo: def get_bar(self): print('wupeiqi') def set_bar(self, value): # 必须两个参数 print('set value' + value) def del_bar(self): print('del wupeiqi') BAR = property(get_bar, set_bar, del_bar, 'description...') obj = Foo() obj.BAR # 自动调用第一个参数中定义的方法:get_bar obj.BAR = "alex" # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入 del obj.BAR # 自动调用第三个参数中定义的方法:del_bar方法 obj.BAR.__doc__ # 自动获取第四个参数中设置的值:description...
由于静态字段方式创建属性具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object): def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 def get_price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price def set_price(self, value): self.original_price = value def del_price(self, value): del self.original_price PRICE = property(get_price, set_price, del_price, '价格属性描述...') obj = Goods() obj.PRICE # 获取商品价格 obj.PRICE = 200 # 修改商品原价 del obj.PRICE # 删除商品原价 实例
类成员修饰符--双下滑线
类的所有成员在上一步骤中已经做了详细的介绍,对于每一个类的成员而言都有两种形式:
- 公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能方法,无法继承和被继承。私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)。
1. 修饰字段
(1) 普通字段
- 公有普通字段:类可以访问;类内部可以访问;派生类中可以访问
- 私有普通字段:仅类内部可以访问;
class foo: def __init__(self, name, age): self.name = name self.__age= age # 私有,外部无法直接访问 def show(self): return self.__age obj = foo('alex', 19) print(obj.name) # print(obj.__age) # 报错 ret = obj.show() # 通过类内部的方法可以使用__age print(ret)
(2)静态字段
- 公有静态字段:对象可以访问;类内部可以访问;派生类中可以访问
- 私有静态字段:仅类内部可以访问;
ps:如果想要强制访问私有字段,可以通过 【对象._类名__私有字段名】访问(如:obj._C__foo),不建议强制访问私有成员。
class Foo: __v = '123' #私有静态字段 def __init__(self): pass def show(self): return Foo.__v obj = Foo() # print(obj.__v) # 报错 # Foo.__v # 报错 print(obj.show()) # 通过类内部的方法可以使用Foo.v
2. 修饰方法
class Foo: __v = '123' def __init__(self): pass def __show(self): print('show') def show2(self): self.__show() obj = Foo() obj.show2()
静态方法、类方法、属性方法同样适用,不再赘述。
类的特殊成员
1. __doc__
表示类的描述信息
class Foo: """ 描述类信息,这是用于看片的神奇 """ def func(self): pass print(Foo.__doc__) # 输出:类的描述信息
1. __module__ 和 __class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
1. __init__()
构造方法,通过类创建对象时,自动触发执行。
2. __del__()
析构方法,当对象在内存中被释放时,自动触发执行。
此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
3. __call__()
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__
4. __int__()
int 函数触发执行。
class Foo: def __init__(self): pass def __int__(self): return 111 obj = Foo() print(obj, type(obj)) # <__main__.Foo object at 0x000000000287B160> <class '__main__.Foo'> r = int(obj) # int(对象) 自动执行对象的__int__方法,并将返回值赋值给r print(r) # 111
5. __str__()
str 函数触发执行。如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
class Foo: def __str__(self): return 'alex' obj = Foo() r =str(obj) # str(对象) 自动执行对象的__str__方法,并将返回值赋值给r print(r) # alex print(obj) # print内部会调用obj的__str__方法。 实际分为两步:第一步,print(str(obj));第二部str调用__str__
6. __add__()
两个对象相加时,自动执行第一个对象的__add__方法,并且将第二个对象当做参数传递给入给other。
class Foo: def __init__(self, name, age): self.name = name self.age = age def __add__(self,other): # 系统自动赋值 self=obj1,other=obj2 # return self.age + other.age # 返回值为:39 return Foo(self.name, other.age) obj1 = Foo('alex', 19) obj2 = Foo('eric', 20) r = obj1 + obj2 print(r.name, r.age) # alex 20
7. __dict__
查看类或对象中的所有成员。返回存储对象属性的一个字典,其键为属性名,值为属性的值。
class Province: country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): print('func') # 获取类的成员,即:静态字段、方法、 print(Province.__dict__) # 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None} obj1 = Province('HeBei',10000) print(obj1.__dict__) # 获取 对象obj1 的成员 # 输出:{'count': 10000, 'name': 'HeBei'} obj2 = Province('HeNan', 3888) print(obj2.__dict__) # 获取 对象obj1 的成员 # 输出:{'count': 3888, 'name': 'HeNan'}
8. __getitem__()、__setitem__()、__delitem__()
用于索引操作,如列表(根据索引角标)、字典(根据键)。以上分别表示获取、设置、删除数据
class Foo(object): def __getitem__(self, key): print('__getitem__', key) def __setitem__(self, key, value): print('__setitem__', key, value) def __delitem__(self, key): print('__delitem__', key) obj = Foo() result = obj['k1'] # 自动触发执行 __getitem__ obj['k2'] = 'wupeiqi' # 自动触发执行 __setitem__ del obj['k1'] # 自动触发执行 __delitem__
也用于切片操作(在2.7里使用__getslice__、__setslice__、__delslice__进行切片操作)
class Foo(object): def __getitem__(self, key): print('__getitem__', key, type(key)) if type(key) == slice: print("调用者希望做切片处理") print(key.start, key.stop, key.step) else: print("调用者希望做索引处理") def __setitem__(self, key, value): print('__setitem__', key, value) def __delitem__(self, key): print('__delitem__', key) obj = Foo() obj[0] # 传入的key是<class 'int'>类型 obj[0:5:2] # 传入的keyi是<class 'slice'>类型
9. __iter__() 和__next__()
for循环的本质为:通过其内部的__next__()方法,从迭代器中一个一个往外取值,如下:
obj = iter([11,22,33,44]) while True: val = obj.__next__() print(val)
所有的可迭代对象内部,都有一个__iter__方法(反之,内部有__iter__的对象都是可迭代对象)。当for循环时,可迭代对象先执行自己的__iter__返回一个迭代器。
class Foo(): def __iter__(self): return iter([11, 22, 33, 44, 55]) obj = Foo() for i in obj: print(i)
10. __new__()
见下文
metaclass “类的祖宗”
1、一切都是对象
class Foo(object): def __init__(self): pass obj = Foo()
上述代码中,obj 是通过 Foo 类实例化的对象。其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
那么Foo类对象是由哪个类的 构造方法 创建的呢?
print type(obj) # 输出:<class '__main__.Foo'> 表示,obj对象由Foo类创建。obj是Foo类的一个实例。 print type(Foo) # 输出:<type 'type'> 表示,Foo类对象由type类创建。Foo类对象是type类的一个实例。
所以:Foo类对象 是通过type类的构造方法创建。
2、“Foo类”创建的内部过程
当创建下列类时:
class Foo: # 可写为class Foo(object) object 可写可不写,所有的类都继承object类
def f1(self)
print(123)
内部实际是如下执行的:
def f1(self):
print 'hello wupeiqi'
Foo = type('Foo',(object,), {'func': f1}) # 创建一个type对象,对象名为Foo。即Foo类由type类实例化产生
#type第一个参数:类名
#type第二个参数:当前类的基类(或称父类)。此参数用于解决继承问题
#type第三个参数:类的成员
3、验证
type类的源码是C写的,我们之间看不到。可以自定义type类的一个派生类MyType进行研究。
类中有一个隐含的属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,可以将设置 __metaclass__ =MyType,(这是python2的写法)从而查看 类 创建的过程。

""" 执行下列代码,输出为:123 说明:解释器从上往下解释代码,解释到Foo类的定义时,去执行了MyType类的构造方法 """ class MyType(type): def __init__(self, *args, **kwargs): print(123) class Foo(object, metaclass=MyType): # python3中的写法,设定Foo对象是由MyType实例化而来 def __init__(self): print(789) def func(self): print('hello world')
""" 执行下列代码,输出为: 123 456 说明: 1、解释器从上往下解释代码,解释到Foo类的定义时,去执行了MyType类的构造方法,所以输出123 2、创建obj对象时,Foo()是Foo类对象后加括号,会去执行MyType类的__call__方法,所以输出456 3、Foo类的构造方法,其实是在MyType类的__call__方法中去调用的,这列没有调用,所以789没有输出 """ class MyType(type): def __init__(self, *args, **kwargs): print(123) def __call__(self): print(456) class Foo(object, metaclass=MyType): def __init__(self): print(789) def func(self): print('hello world') obj = Foo()
""" 执行下列代码,输出为: 123 456 789 说明: 所以,在创建对象obj时,不是直接去执行了Foo的构造方法,而是经历了几步别的代码 """ class MyType(type): def __init__(self, what, bases=None, dict=None): print(123) super(MyType, self).__init__(what, bases, dict) def __call__(self, *args, **kwargs): print(456) # self = Foo obj = self.__new__(self, *args, **kwargs) # 调用Foo中的__new__方法 self.__init__(obj) # 执行Foo的构造方法__init__ class Foo(object, metaclass=MyType): # __metaclass__ = MyType # python2中定义metaclass的写法 def __init__(self): print(789) def __new__(cls, *args, **kwargs): print("aaa") return object.__new__(cls, *args, **kwargs) # obj 其实是在__new__里面创建的 obj = Foo()
参考:
http://www.cnblogs.com/wupeiqi/p/4766801.html
http://www.cnblogs.com/alex3714/articles/5213184.html
浙公网安备 33010602011771号