python 面向对象(进阶)
本节大纲:
类的成员
类成员的修饰符
类的特殊成员
类的成员
类的成员可以分为三大类:字段、方法、和属性
注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
一.字段
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同。
- 普通字段属于对象
- 静态字段属于类
class Province: country="中国" def __init__(self,name): self.name=name obj=Province("河北省") # 直接访问普通字段 print(obj.name) #直接访问静态字段 print(Province.country)
由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。其在内容的存储方式类似如下图:
由上图可是:
- 静态字段在内存中只保存一份
- 普通字段在每个对象中都要保存一份
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
二.方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
- 静态方法:由类调用;无默认参数;
#!/usr/bin/env python # -*- coding:utf-8 -*- class Foo: def __init__(self,name): self.name=name def ord_func(self): """定义普通方法至少有一个self参数""" print("普通方法") #print(self.name) @classmethod def class_func(cls): """定义类方法,至少有一个cls参数""" print("类方法") @staticmethod def static_func(): """定义静态方法,无默认参数""" print("静态方法") f=Foo("qxw") #调用普通方法 f.ord_func() #调用类方法 Foo.class_func() #调用静态方法 Foo.static_func()
相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。
三.属性
对于属性(普通方法的变种),有以下三个知识点:
- 属性的基本使用
- 属性的两种定义方式
1.属性的基本使用
# ############### 定义 ############### class Foo: def func(self): pass # 定义属性 @property def prop(self): pass # ############### 调用 ############### foo_obj = Foo() foo_obj.func() foo_obj.prop #调用属性
由属性的定义和调用要注意一下几点:
- 定义时,在普通方法的基础上添加 @property 装饰器;
- 定义时,属性仅有一个self参数
- 调用时,无需括号
方法:foo_obj.func()
属性:foo_obj.prop
注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象
属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。
2.属性的两种定义方式
属性的定义有两种方式:
- 装饰器 即:在方法上应用装饰器
- 静态字段 即:在类中定义值为property对象的静态字段
装饰器方式:在类的普通方法上应用@property装饰器
新式类,具有三种@property装饰器
#!/usr/bin/env python # -*- coding:utf-8 -*- 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.deleter def price(self): del self.original_price obj=Goods() obj.price=20 del obj.price
注:经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法
property的构造方法中有个四个参数
- 第一个参数是方法名,调用
对象.属性
时自动触发执行方法 - 第二个参数是方法名,调用
对象.属性 = XXX
时自动触发执行方法 - 第三个参数是方法名,调用
del 对象.属性
时自动触发执行方法 - 第四个参数是字符串,调用
对象.属性.__doc__
,此参数是该属性的描述信息
#!/usr/bin/env python # -*- coding:utf-8 -*- class Foo: def get_bar(self): return 'hello world' # *必须两个参数 def set_bar(self, value): return 'set value' + value def del_bar(self): return 'qinxiaowei' BAR=property(get_bar, set_bar, del_bar, 'description...') obj=Foo() print(obj.BAR) obj.BAR="QXW" del Foo.BAR
由于静态字段方式创建属性具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
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): del self.original_price PRICE=property(get_price,set_price,del_price,'价格描述.....') foo=Goods() print(foo.PRICE.__doc__)
类成员的修饰符
类的所有成员在上一步骤中已经做了详细的介绍,对于每一个类的成员而言都有两种形式:
- 公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能方法
私有成员和公有成员的定义不同:私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)
1
2
3
4
5
|
class C: def __init__( self ): self .name = '公有字段' self .__foo = "私有字段" |
私有成员和公有成员的访问限制不同:
静态字段
- 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
- 私有静态字段:仅类内部可以访问;

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() # 派生类中可以访问 21 22 公有静态字段

class C: __name = "公有静态字段" def func(self): print C.__name class D(C): def show(self): print C.__name C.__name # 类访问 ==> 错误 obj = C() obj.func() # 类内部可以访问 ==> 正确 obj_son = D() obj_son.show() # 派生类中可以访问 ==> 错误 私有静态字段
普通字段
- 公有普通字段:对象可以访问,类内部可以访问,派生类可以访问
- 私有普通字段:仅类内部可以访问;
ps:如果想要强制访问私有字段,可以通过 【对象._类名__私有字段明 】访问(如:obj._C__foo),不建议强制访问私有成员。

class C: def __init__(self): self.name="public string" def func(self): print(self.name) class D(C): def show(self): print(self.name) #通过对象访问 foo=C() foo.func() #通过派生类访问 fod=D() fod.show()

class C: def __init__(self): self.__name="private string" def func(self): print(self.__name) class D(C): def show(self): print(self.__name) #通过对象访问 foo=C() foo.func() #通过派生类访问 fod=D() fod.show()
方法、属性的访问于上述方式相似,即:私有成员只能在类内部使用
ps:非要访问私有属性的话,可以通过 对象._类__属性名
类的特殊成员
1. __doc__ (表示类的描述信息)
2.__module__ 和 __class__
__module__:表示当前操作对象所在的模块
__class__:表示当前操作对象所在的类
3.__init__
构造方法,通过类创建对象时,自动触发执行。
4. __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
5. __call__
对象后面加括号,触发执行
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print '__call__' obj = Foo() # 执行 __init__ obj() # 执行 __call__
6. __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__) obj = Province('HeBei',10000) #获取对象成员 print(obj.__dict__)
7. __str__
如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
class Foo: def __str__(self): return "hello,world" obj=Foo() print(obj)
8.__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据

class method: def __getitem__(self, key): print("__getitem__",key) def __setitem__(self, key, value): print("__setitem__",key,value) def __delitem__(self, key): print("__delitem__",key) f=method() #自动触发执行__setitem__ f["name"]="qinxiaowei" #自动触发执行__getitem__ f["name"] #自动触发执行__delitem__ del f["name"]
9.__getslice__、__setslice__、__delslice__
该三个方法用于分片操作,如:列表
10.
10. __iter__
用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__

obj=iter([11,22,33,44,]) while True: try: va1=obj.__next__() print(va1) except Exception as e: break