之前的基础篇里有提到继承,封装,以及类的定义。这个进阶篇,就是想更深入的探讨一下类中的成员以及一些可能你这辈子编程也不会遇到的特殊情况。
1.类的成员
类的成员可以分为三大类:字段、方法和属性
注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
一、字段
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,
- 普通字段属于对象
- 静态字段属于类
class Province: # 静态字段 country = '中国' def __init__(self, name): # 普通字段 self.name = name # 直接访问普通字段 obj = Province('河北省') print obj.name # 直接访问静态字段 Province.country 字段的定义和使用
由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。其在内容的存储方式类似如下图:
由上图可是:
- 静态字段在内存中只保存一份
- 普通字段在每个对象中都要保存一份
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
除了以上还有公有属性和私有属性之分(因为两位讲师完全不同的命名,所以姑且就遵循他们二人的意志,这里的属性和字段都是一种类型,也可以叫做公有字段和私有字段吧)
- 公有字段
- 私有字段
class person(object): def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex self.__heart = "normal" #定义了私有字段 d = person("张三丰","100","男",) print(d.__heart) #调用私有字段
结果会报错,说找不到这个字段。所以,我们应该怎么找到这个字段呢?看下面这段代码。
class Person(object): def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex self.__heart = "正常" #定义了私有字段 def Get_shot(self): print("%s被打死了!"%self.name) self.__heart = "心脏停止运动" print(self.__heart) #调用私有字段 d = Person("张三丰","100","男",) d.Get_shot()
从上面的例子可以看出,当我们想获取私有字段时,需要重新定义一个方法来调用这个私有的字段。而且这方法是只读,在外部是无法改变私有字段的。如果想在外部改变私有字段的话,以上段代码为例,可以使用 d._Person__heart=(但是,并不推荐你使用这种方法)
静态字段就是公有字段。说明,所有属于这个类的对象,都可以获取到这个字段。仔细看下面这段代码。
1 class Person(object): 2 3 country = "中国" 4 5 def __init__(self,name,age,sex): 6 self.name = name 7 self.age = age 8 self.sex = sex 9 self.__heart = "正常" #定义了私有字段 10 11 def Get_shot(self): 12 print("%s被打死了!"%self.name) 13 self.__heart = "心脏停止运动" 14 print(self.__heart) #调用私有字段 15 16 d = Person("张三丰","100","男",) 17 l = Person("朱元璋","60","男") 18 19 print(d.country) 20 print(l.country) 21 22 print("世界都是我们韩国的") 23 Person.country = "韩国" 24 print(d.country) 25 print(l.country) 26 27 print("张三丰有骨气不愿意当韩国人") 28 d.country = "中国" 29 print(d.country) 30 print(l.country)
"""结果如下"""
中国
中国
世界都是我们韩国的
韩国
韩国
张三丰有骨气不愿意当韩国人
中国
韩国
会发现当我们通过类去改变公有字段时,对象获取到的字段的值都是改变后的值。如果只是其中一个对象改变了公有字段,对另一个对象并没有影响取的值还是原来的值。
原理是,当对象改变公有字段时,内存会开辟一块空间来存储被改变的公有字段。开始获取公有字段时,会去找有没有被改变的字段的空间,如果没有就再去找类中的公有字段。
二、方法
- 静态方法
通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Dog( object ): def __init__( self ,name): self .name = name @staticmethod #把eat方法变为静态方法 def eat( self ): print ( "%s is eating" % self .name) d = Dog( "ChenRonghua" ) d.eat() |
上面的调用会出以下错误,说是eat需要一个self参数,但调用时却没有传递,没错,当eat变成静态方法后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了。
1
2
3
4
5
|
Traceback (most recent call last): File "/Users/jieli/PycharmProjects/python基础/自动化day7面向对象高级/静态方法.py" , line 17 , in <module> d.eat() TypeError: eat() missing 1 required positional argument: 'self' < / module> |
想让上面的代码可以正常工作有两种办法
1. 调用时主动传递实例本身给eat方法,即d.eat(d)
2. 在eat方法中去掉self参数,但这也意味着,在eat中不能通过self.调用实例中的其它变量了
1 class Dog(object): 2 3 def __init__(self,name): 4 self.name = name 5 6 @staticmethod 7 def eat(): 8 print(" is eating") 9 10 11 12 d = Dog("ChenRonghua") 13 d.eat()
- 类方法
类方法通过@classmethod装饰器实现,类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量
1
2
3
4
5
6
7
8
9
10
11
12
|
class Dog( object ): def __init__( self ,name): self .name = name @classmethod def eat( self ): print ( "%s is eating" % self .name) d = Dog( "ChenRonghua" ) d.eat() |
执行报错如下,说Dog没有name属性,因为name是个实例变量,类方法是不能访问实例变量的
1
2
3
4
5
6
|
Traceback (most recent call last): File "/Users/jieli/PycharmProjects/python基础/自动化day7面向对象高级/类方法.py" , line 16 , in <module> d.eat() File "/Users/jieli/PycharmProjects/python基础/自动化day7面向对象高级/类方法.py" , line 11 , in eat print ( "%s is eating" % self .name) AttributeError: type object 'Dog' has no attribute 'name' |
此时可以定义一个类变量,也叫name,看下执行效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
class Dog( object ): name = "我是类变量" def __init__( self ,name): self .name = name @classmethod def eat( self ): print ( "%s is eating" % self .name) d = Dog( "ChenRonghua" ) d.eat() #执行结果 我是类变量 is eating |
- 属性方法
属性方法的作用就是通过@property把一个方法变成一个静态属性
1
2
3
4
5
6
7
8
9
10
11
12
|
class Dog( object ): def __init__( self ,name): self .name = name @property def eat( self ): print ( " %s is eating" % self .name) d = Dog( "ChenRonghua" ) d.eat() |
调用会出以下错误, 说NoneType is not callable, 因为eat此时已经变成一个静态属性了, 不是方法了, 想调用已经不需要加()号了,直接d.eat就可以了
1
2
3
4
5
|
Traceback (most recent call last): ChenRonghua is eating File "/Users/jieli/PycharmProjects/python基础/自动化day7面向对象高级/属性方法.py" , line 16 , in <module> d.eat() TypeError: 'NoneType' object is not callable |
正常调用如下
1
2
3
4
5
|
d = Dog( "ChenRonghua" ) d.eat 输出 ChenRonghua is eating |
好吧,把一个方法变成静态属性有什么卵用呢?既然想要静态变量,那直接定义成一个静态变量不就得了么?well, 以后你会需到很多场景是不能简单通过 定义 静态属性来实现的, 比如 ,你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了, 想知道这种状态你必须经历以下几步:
1. 连接航空公司API查询
2. 对查询结果进行解析
3. 返回结果给你的用户
因此这个status属性的值是一系列动作后才得到的结果,所以你每次调用时,其实它都要经过一系列的动作才返回你结果,但这些动作过程不需要用户关心, 用户只需要调用这个属性就可以,明白 了么?
1 class Flight(object): 2 def __init__(self,name): 3 self.flight_name = name 4 5 6 def checking_status(self): 7 print("checking flight %s status " % self.flight_name) 8 return 1 9 10 @property 11 def flight_status(self): 12 status = self.checking_status() 13 if status == 0 : 14 print("flight got canceled...") 15 elif status == 1 : 16 print("flight is arrived...") 17 elif status == 2: 18 print("flight has departured already...") 19 else: 20 print("cannot confirm the flight status...,please check later") 21 22 23 f = Flight("CA980") 24 f.flight_status
cool , 那现在我只能查询航班状态, 既然这个flight_status已经是个属性了, 那我能否给它赋值呢?试试吧
1
2
3
|
f = Flight( "CA980" ) f.flight_status f.flight_status = 2 |
输出, 说不能更改这个属性,我擦。。。。,怎么办怎么办。。。
1
2
3
4
5
6
|
checking flight CA980 status flight is arrived... Traceback (most recent call last): File "/Users/jieli/PycharmProjects/python基础/自动化day7面向对象高级/属性方法.py" , line 58 , in <module> f.flight_status = 2 AttributeError: can't set attribute |
当然可以改, 不过需要通过@proerty.setter装饰器再装饰一下,此时 你需要写一个新方法, 对这个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 = { : "canceled", :"arrived", : "departured" } print("