Python学习之路12——面向对象进阶
一、静态方法
通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法,什么是静态方法呢?
其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量。
但静态方法是不可以访问实例变量或类变量的,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法;
静态方法不可访问实例变量跟类变量,内部其他函数可通过self.静态方法执行,需要实例化
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 6 # 静态方法 7 class Schoolmate(object): 8 def __init__(self, name): 9 self.name = name 10 11 @staticmethod # 把eat方法变为静态方法 12 def eat(self): 13 print("%s is eating" % self.name) 14 15 16 p = Schoolmate("VisonWong") 17 p.eat() 18 19 20 # Traceback (most recent call last): 21 # File "E:/Python/PythonLearing/class/静态方法.py", line 17, in <module> 22 # p.eat() 23 # TypeError: eat() missing 1 required positional argument: 'self'
上面的调用会出以下错误,说是eat需要一个self参数,但调用时却没有传递。
当eat变成静态方法后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了。
想让上面的代码正常工作有两种办法
-
- 调用时主动传递实例本身给eat方法,即p.eat(p)
- 在eat方法中去掉self参数,但这也意味着,在eat中不能通过self.调用实例中的其它变量了
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 # 静态方法 6 class Schoolmate(object): 7 def __init__(self, name): 8 self.name = name 9 10 @staticmethod # 把ea变成静态方法 11 def eat(self): 12 print("%s is eating" % self.name) 13 14 15 p = Schoolmate("VisonWong") 16 p.eat(p) #变成静态方法后,调用时不会自动传递实例参数,需主动传递 17 18 19 # VisonWong is eating
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 6 # 静态方法 7 class Schoolmate(object): 8 def __init__(self, name): 9 self.name = name 10 11 @staticmethod # 把eat方法变为静态方法 12 def eat(): 13 print("eating") 14 15 16 p = Schoolmate("visonwong") 17 p.eat() 18 19 20 # eating
内部其他函数可通过self.静态方法调用,需实例化。
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 6 # 静态方法 7 class Schoolmate(object): 8 def __init__(self, name): 9 self.name = name 10 11 def resting(self): 12 print("%s is resting" %self.name) 13 self.sleeping(self) #内部调用静态方法 14 15 @staticmethod # 把eat方法变为静态方法 16 def sleeping(self): 17 print("%s is eating" %self.name) 18 19 20 p = Schoolmate("VisonWong") 21 p.resting() 22 23 24 # VisonWong is resting 25 # VisonWong is eating
二、类方法
类方法顾名思义跟类有关,类方法通过@classmethod装饰器实现,
类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量,不用实例化,通过类名直接执行。
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 # 类方法 6 class Schoolmate(object): 7 def __init__(self, name): 8 self.name = name 9 10 @classmethod # 把eat方法变为类方法 11 def eat(self): 12 print("%s is eating" % self.name) 13 14 15 p = Schoolmate("visonwong") 16 p.eat() 17 18 19 # Traceback (most recent call last): 20 # File "E:/Python/PythonLearing/class/类方法.py", line 16, in <module> 21 # p.eat() 22 # File "E:/Python/PythonLearing/class/类方法.py", line 12, in eat 23 # print("%s is eating" % self.name) 24 # AttributeError: type object 'Schoolmate' has no attribute 'name'
执行报错如下,说schoolmat没有name属性,这是因为name是个实例变量,类方法是不能访问实例变量的,只能访问类变量
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 # 类方法 6 class Schoolmate(object): 7 name = "Schoolmate的类变量" 8 9 def __init__(self, name): 10 self.name = name 11 12 @classmethod # 把eat方法变为类方法 13 def eat(self): 14 print("%s is eating" % self.name) 15 16 17 p = Schoolmate("visonwong") 18 p.eat() 19 20 21 # Schoolmate的类变量 is eating
此时定义一个名为name的类变量,即可解决。
三、属性方法
属性方法的作用就是通过@property把一个方法变成一个静态属性(变量)。
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 # 属性方法 6 7 class Schoolmate(object): 8 name = ("Schoolmat的类变量") 9 10 def __init__(self, name): 11 self.name = name 12 13 @property # 把eat方法变为属性方法 14 def eat(self): 15 print("%s is eating" % self.name) 16 17 18 p = Schoolmate("visonwong") 19 p.eat() 20 21 22 # Traceback (most recent call last): 23 # File "E:/Python/PythonLearing/class/属性方法.py", line 19, in <module> 24 # p.eat() 25 # TypeError: 'NoneType' object is not callable
调用会出现以下错误, NoneType is not callable, 因为eat此时已经变成一个静态属性了, 不是方法了。
想调用已经不需要加()号了,直接p.eat就可以了.
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 # 属性方法 6 7 class Schoolmate(object): 8 name = ("Schoolmat的类变量") 9 10 def __init__(self, name): 11 self.name = name 12 13 @property # 把eat方法变为属性方法 14 def eat(self): 15 print("%s is eating" % self.name) 16 17 18 p = Schoolmate("visonwong") 19 p.eat 20 21 22 # visonwong is eating
好吧,把一个方法变成静态属性有什么卵用呢?
既然想要静态变量,那直接定义成一个静态变量不就得了么?
但是,以后我们会遇到静态变量不固定或者变量值需要通过外部接口查询的情况,不能简单通过定义静态属性来实现。
比如 ,你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了?
想知道这种状态你必须经历以下几步:
-
- 连接航空公司API查询
- 对查询结果进行解析
- 返回结果给你的用户
因此航班状态属性的值是一系列动作后才得到的结果,所以你每次调用时,其实它都要经过一系列的动作才返回你结果。
然而这些动作过程不需要用户关心, 用户只需要调用这个属性就可以,明白了么?
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 6 # 属性方法实例 7 8 class Flight(object): 9 def __init__(self, name): 10 self.flight_name = name 11 12 def checking_status(self): #模拟整个查询过程,并返回结果 13 print("checking flight %s status " % self.flight_name) 14 return 1 15 16 @property 17 def flight_status(self): 18 status = self.checking_status() #模拟调用航空公司API接口 19 if status == 0: 20 print("flight got canceled...") 21 elif status == 1: 22 print("flight is arrived...") 23 elif status == 2: 24 print("flight has departured already...") 25 else: 26 print("cannot confirm the flight status...,please check later") 27 28 29 f = Flight("CA980") 30 f.flight_status 31 32 33 # checking flight CA980 status 34 # flight is arrived...
cool , 那现在我只能查询航班状态, 既然这个flight_status已经是个属性了, 那我能否给它赋值呢?试试吧。
1 f = Flight("CA980") 2 f.flight_status 3 4 f.flight_status = 2 5 6 7 # Traceback (most recent call last): 8 # File "E:/Python/PythonLearing/class/flight_status.py", line 32, in <module> 9 # f.flight_status = 2 10 # AttributeError: can't set attribute
输出显示不能更改这个属性。
修改方法是需要通过@proerty.setter装饰器再装饰一下,此时 你需要写一个新方法, 对这个flight_status进行更改。
通过@property.deleter可以删除类中的任意变量(除了静态属性本身)。
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 6 # 属性方法实例 7 8 class Flight(object): 9 def __init__(self, name): 10 self.flight_name = name 11 self.__status = None #定义航班状态查询变量,储存属性方法中静态变量的值(相当于静态变量的“替身”) 12 self.__check = None #定义航班状态查询变量 13 14 def checking_status(self): #模拟整个查询过程,并返回结果 15 print("checking flight %s status " % self.flight_name) 16 self.__check = True 17 return 1 18 19 @property 20 def flight_status(self): 21 if not self.__check: #如果未查询,调用航空API接口,如果已调用,采用后续修改值 22 self.__status = self.checking_status() #模拟调用航空公司API接口 23 if self.__status == 0: 24 print("flight got canceled...") 25 elif self.__status == 1: 26 print("flight is arrived...") 27 elif self.__status == 2: 28 print("flight has departured already...") 29 else: 30 print("cannot confirm the flight status...,please check later") 31 32 @flight_status.setter #修改静态方法 33 def flight_status(self,status): 34 self.__status = status 35 status_dic = { 36 0:"canceled", 37 1:"arrived", 38 2:"departured" 39 } 40 print('\033[31;7mHas changed the flight status to\033[0m',status_dic.get(status)) 41 42 @flight_status.deleter #删除静态方法 43 def flight_status(self): 44 del self.__status 45 print("status get removed..") 46 47 48 49 f = Flight("CA980") 50 f.flight_status 51 52 f.flight_status = 2 #触发@flight_status.setter 53 f.flight_status 54 55 del f.flight_status #触发@flight_status.deleter 56 # f.flight_status 57 58 59 # checking flight CA980 status 60 # flight is arrived... 61 # Has changed the flight status to departured 62 # flight has departured already... 63 # status get removed..
四、类的特殊成员方法
1、 __doc__ 表示类的描述信息
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 6 # __doc__ 7 8 class Foo: 9 """ 描述类信息""" 10 11 def __init__(self): 12 pass 13 14 def func(self): 15 pass 16 17 18 print(Foo.__doc__) 19 20 #描述类信息
2、__module__和__class__
-
- __module__ 表示当前操作的对象在那个模块
- __class__ 表示当前操作的对象的类是什么
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 6 # __doc__ 7 8 class Foo: 9 """ 描述类信息""" 10 11 def __init__(self): 12 pass 13 14 def func(self): 15 pass 16 17 18 print(Foo.__module__) 19 print(Foo.__class__) 20 21 22 # __main__ 23 # <class 'type'>
3、__init__ 构造方法,通过类创建对象时,自动触发执行
4、__del__析构方法,当对象在内存中被释放时,自动触发执行
5、__call__ 对象后面加括号,触发执行
注:__init__的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 6 # __doc__ 7 8 class Foo: 9 """ 描述类信息""" 10 11 def __init__(self): 12 print('__init__') 13 14 def func(self): 15 pass 16 17 def __call__(self, *args, **kwargs): 18 print('__call__') 19 20 21 22 f = Foo() #执行__init__方法 23 f() #执行__call__方法 24 25 26 # __init__ 27 # __call__
6、 __dict__ 查看类或对象中的所有成员
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 class Province: 6 country = 'China' 7 8 def __init__(self, name, count): 9 self.name = name 10 self.count = count 11 12 def func(self, *args, **kwargs): 13 print('whatever') 14 15 16 # 获取类的成员,即:静态字段、方法、 17 print(Province.__dict__) 18 # 输出:{'__init__': <function Province.__init__ at 0x0054D588>, '__dict__': <attribute '__dict__' of 'Province' objects>, 19 # '__doc__': None, 'func': <function Province.func at 0x0054D4B0>, '__weakref__': <attribute '__weakref__' of 'Province' objects>, 20 # 'country': 'China', '__module__': '__main__'} 21 22 obj1 = Province('HeBei', 10000) 23 print(obj1.__dict__) 24 # 获取 对象obj1 的成员 25 # 输出:{'count': 10000, 'name': 'HeBei'}
7、 __str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 # __str__ 6 7 class Foo: 8 def __str__(self): 9 return 'visonwong' 10 11 12 obj = Foo() 13 print(obj) # 输出__str__返回值 而不是内存地址 14 15 16 # 输出:visonwong
8、__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据。
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 # __getitem__、__setitem__、__delitem__ 6 7 class Foo(object): 8 def __init__(self,name,value): 9 self.name = name 10 self.value = value 11 self['B'] = "BB" #自动触发_setitem__方法 12 self['D'] = "DD" #自动触发_setitem__方法 13 del self['D'] #自动触发__delitem__方法 14 15 def __setitem__(self, name, value): 16 17 #每当属性被赋值的时候都会调用该方法,因此不能再该方法内赋值 self.name = value 会死循环 18 19 print("__setitem__:Set %s Value %s" % (name, value)) 20 self.__dict__[name] = value 21 22 def __getitem__(self, name): 23 24 #当访问不存在的属性时会调用该方法 25 26 print("__getitem__:No attribute named '%s'" % name) 27 return None 28 29 def __delitem__(self, name): 30 31 #当删除属性时调用该方法 32 33 print("__delitem__:Delect attribute '%s'" % name) 34 del self.__dict__[name] 35 print(self.__dict__) 36 37 38 if __name__ == "__main__": 39 f = Foo('viosn',100) 40 b = f['bb'] #自动触发__getitem__方法 41 42 43 # __setitem__:Set B Value BB 44 # __setitem__:Set D Value DD 45 # __delitem__:Delect attribute 'D' 46 # {'name': 'viosn', 'value': 100, 'B': 'BB'} 47 # __getitem__:No attribute named 'bb'
9、__new__ \ __metaclass__
上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象;
如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的构造方法创建。
1 print type(f) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 2 print type(Foo) # 输出:<type 'type'> 表示,Foo类对象由 type 类创建
所以,f对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建,那么类还有一种创建方式。
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 6 def func(self): 7 print("hello %s" % self.name) 8 9 # 加上构造方法 10 def __init__(self, name, age): 11 self.name = name 12 self.age = age 13 14 15 Foo = type('Foo', (object,), {'func': func, '__init__': __init__}) 16 17 f = Foo("jack", 22) 18 f.func() 19 20 21 # hello jack