本节内容

  1. 面向对象高级语法

    •  经典类 vs 新式类  

    •  静态方法、类方法、属性方法

    •  类的特殊方法

    •  反射

  2. 异常处理

面向对象高级语法

经典类 vs 新式类

  • 经典类:深度优先
  • 新式类:广度优先
  • super()用法

 抽象接口

...

静态方法

通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法

 1 class Dog(object):
 2  
 3     def __init__(self,name):
 4         self.name = name
 5  
 6     @staticmethod #把eat方法变为静态方法
 7     def eat(self):
 8         print("%s is eating" % self.name)
 9  
10  
11  
12 d = Dog("嘻哈")
13 d.eat()

 

上面的调用会出以下错误,说是eat需要一个self参数,但调用时却没有传递,没错,当eat变成静态方法后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了。

Traceback (most recent call last):
  File "/Users/jieli/PycharmProjects/python基础/面向对象高级/静态方法.py", line 17, in <module>
    d.eat()
TypeError: eat() missing 1 required positional argument: 'self'

 想让上面的代码正常工作有两种办法

  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 class Dog(object):
 2     def __init__(self,name):
 3         self.name = name
 4  
 5     @classmethod
 6     def eat(self):
 7         print("%s is eating" % self.name)
 8  
 9  
10  
11 d = Dog("ChenRonghua")
12 d.eat()

执行报错如下,name 是个实例变量,类方法是不能访问实例变量的

Traceback (most recent call last):
  File "D:/编程学习/python/练习/基础/类/属性方法.py", line 57, in <module>
    d.eat()
  File "D:/编程学习/python/练习/基础/类/属性方法.py", line 53, in eat
    print("%s is eating" % self.name)
AttributeError: type object 'Dog' has no attribute 'name'

 此时可以定义一个类变量,也叫name,看下执行效果:

 1 class Dog(object):
 2     name = "我是类变量"
 3     def __init__(self,name):
 4         self.name = name
 5  
 6     @classmethod
 7     def eat(self):
 8         print("%s is eating" % self.name)
 9  
10  
11  
12 d = Dog("ChenRonghua")
13 d.eat()
14  
15  
16 #执行结果
17  
18 我是类变量 is eating

属性方法

属性方法的作用就是通过 @property 把一个方法变成一个静态属性

 1 class Dog(object):
 2  
 3     def __init__(self,name):
 4         self.name = name
 5  
 6     @property
 7     def eat(self):
 8         print(" %s is eating" %self.name)
 9  
10  
11 d = Dog("ChenRonghua")
12 d.eat()

 

调用会出以下错误,因为 eat 此时已经变成一个静态属性了,不是方法了,想调用就不需要加()号了,直接 d.eat 就可以了

Traceback (most recent call last):
 ChenRonghua is eating
  File "D:/编程学习/python/练习/基础/类/属性方法.py", line 75, in <module>
    d.eat()
TypeError: 'NoneType' object is not callable

 正常调用:

1 d = Dog("ChenRonghua")
2 d.eat
3  
4 输出
5  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 f = Flight("CA980")
2 f.flight_status
3 f.flight_status =  2

 

输出,报错,说是不能更改属性

1 checking flight CA980 status 
2 flight is arrived...
3 Traceback (most recent call last):
4   File "D:/编程学习/python/练习/基础/类/属性方法.py", line 102, in <module>
5     f.flight_status =  2
6 AttributeError: can't set attribute

 

当然可以改,不过需要通过 @proerty.setter 装饰器再装饰一下,此时你需要写一个新方法,对 flight_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 
11     @property
12     def flight_status(self):
13         status = self.checking_status()
14         if status == 0 :
15             print("flight got canceled...")
16         elif status == 1 :
17             print("flight is arrived...")
18         elif status == 2:
19             print("flight has departured already...")
20         else:
21             print("cannot confirm the flight status...,please check later")
22     
23     @flight_status.setter #修改
24     def flight_status(self,status):
25         status_dic = {
26             0 : "canceled",
27             1 :"arrived",
28             2 : "departured"
29         }
30         print("\033[31;1mHas changed the flight status to \033[0m",status_dic.get(status) )
31 
32     @flight_status.deleter  #删除
33     def flight_status(self):
34         print("status got removed...")
35 
36 f = Flight("CA980")
37 f.flight_status
38 f.flight_status =  2 #触发@flight_status.setter 
39 del f.flight_status #触发@flight_status.deleter 
View Code

 

注意以上代码里还写了一个 @flight_status.deleter ,是允许可以将这个属性删除

类的特殊成员方法

1. __doc__    表示类的描述信息

1 class Foo:
2     """ 描述类信息,这是用于看片的神奇 """
3  
4     def func(self):
5         pass
6  
7 print Foo.__doc__
8 #输出:类的描述信息
View Code

 

2. __module__   表示当前操作的对象在哪一个模块

    __class__   表示当前操作对象在哪一个类

1 class C:
2 
3     def __init__(self):
4         self.name = 'wupeiqi'
lib/aa.py

 

1 from lib.aa import C
2 
3 obj = C()
4 print obj.__module__  # 输出 lib.aa,即:输出模块
5 print obj.__class__      # 输出 lib.aa.C,即:输出类
index.py

 

3. __init__   构造方法,通过类创建对象时,自行触发执行

4. __del__   析构方法,当对象在内存中被释放时,自动触发执行

 注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的

5. __call__   对象后面加括号,触发执行

 注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

 1 class Foo:
 2  
 3     def __init__(self):
 4         pass
 5      
 6     def __call__(self, *args, **kwargs):
 7  
 8         print '__call__'
 9  
10  
11 obj = Foo() # 执行 __init__
12 obj()       # 执行 __call__
View Code

 

6. __dict__   查看类或对象中的所有成员

 1 class Province:
 2  
 3     country = 'China'
 4  
 5     def __init__(self, name, count):
 6         self.name = name
 7         self.count = count
 8  
 9     def func(self, *args, **kwargs):
10         print 'func'
11  
12 # 获取类的成员,即:静态字段、方法、
13 print Province.__dict__
14 # 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}
15  
16 obj1 = Province('HeBei',10000)
17 print obj1.__dict__
18 # 获取 对象obj1 的成员
19 # 输出:{'count': 10000, 'name': 'HeBei'}
20  
21 obj2 = Province('HeNan', 3888)
22 print obj2.__dict__
23 # 获取 对象obj1 的成员
24 # 输出:{'count': 3888, 'name': 'HeNan'}
View Code

 

7. __str__   如果一个类中定义了 __str__ 方法,那么在打印对象时,默认输出该方法的返回值

1 class Foo:
2  
3     def __str__(self):
4         return 'hello'
5  
6  
7 obj = Foo()
8 print obj
9 # 输出:hello
View Code

 

8. __getitem__、__setitem__、__delitem__   用于索引操作,如字典。它们分别表示获取、设置、删除数据

 1 class Foo(object):
 2  
 3     def __getitem__(self, key):
 4         print('__getitem__',key)
 5  
 6     def __setitem__(self, key, value):
 7         print('__setitem__',key,value)
 8  
 9     def __delitem__(self, key):
10         print('__delitem__',key)
11  
12  
13 obj = Foo()
14  
15 result = obj['k1']      # 自动触发执行 __getitem__
16 obj['k2'] = 'alex'   # 自动触发执行 __setitem__
17 del obj['k1'] 
View Code

 

9. __new__、__metaclass__

参考:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 得票最高的答案写的非常好

1 class Foo(object):
2  
3  
4     def __init__(self,name):
5         self.name = name
6  
7  
8 f = Foo("alex")

 

上述的代码中,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 类的构造方法创建。那么创建类就有两种方式:

a. 普通方式

1 class Foo(object):
2   
3     def func(self):
4         print 'hello alex'

 

b. 特殊方式

1 def func(self):
2     print 'hello wupeiqi'
3   
4 Foo = type('Foo',(object,), {'func': func})
5 #type第一个参数:类名
6 #type第二个参数:当前类的基类
7 #type第三个参数:类的成员

 

 1 def func(self):
 2     print("hello %s"%self.name)
 3 
 4 def __init__(self,name,age):
 5     self.name = name
 6     self.age = age
 7 Foo = type('Foo',(object,),{'func':func,'__init__':__init__})
 8 
 9 f = Foo("jack",22)
10 f.func()

 

所以,类是 type 类实例化产生的

问:类默认是由 type 实例化产生的,type 类中如何实现创建类?类又是如何创建对象?

答:类中有一个属性 __metaclass__,其表示该类由谁来实例化创建,所以,我们可以为 __metaclass__ 设置一个 type 类的派生类,从而查看类创建的过程。

 1 class MyType(type):
 2     def __init__(self,*args,**kwargs):
 3 
 4         print("Mytype __init__",*args,**kwargs)
 5 
 6     def __call__(self, *args, **kwargs):
 7         print("Mytype __call__", *args, **kwargs)
 8         obj = self.__new__(self)
 9         print("obj ",obj,*args, **kwargs)
10         print(self)
11         self.__init__(obj,*args, **kwargs)
12         return obj
13 
14     def __new__(cls, *args, **kwargs):
15         print("Mytype __new__",*args,**kwargs)
16         return type.__new__(cls, *args, **kwargs)
17 
18 print('here...')
19 class Foo(object,metaclass=MyType):
20 
21 
22     def __init__(self,name):
23         self.name = name
24 
25         print("Foo __init__")
26 
27     def __new__(cls, *args, **kwargs):
28         print("Foo __new__",cls, *args, **kwargs)
29         return object.__new__(cls)
30 
31 f = Foo("Alex")
32 print("f",f)
33 print("fname",f.name)
自定义元素

 

类的生成、调用顺序依次是 __new__ --> __init__ --> __call__

反射

通过字符串映射或修改程序运行时的状态、属性、方法,有以下4种方法

1 def getattr(object, name, default=None): # known special case of getattr
2     """
3     getattr(object, name[, default]) -> value
4     
5     Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
6     When a default argument is given, it is returned when the attribute doesn't
7     exist; without it, an exception is raised in that case.
8     """
9     pass
getattr(object, name, default=None)

 

1 判断object中有没有一个name字符串对应的方法或属性
hasattr(object,name)

 

1 def setattr(x, y, v): # real signature unknown; restored from __doc__
2     """
3     Sets the named attribute on the given object to the specified value.
4     
5     setattr(x, 'y', v) is equivalent to ``x.y = v''
setattr(x, y, v)

 

1 def delattr(x, y): # real signature unknown; restored from __doc__
2     """
3     Deletes the named attribute from the given object.
4     
5     delattr(x, 'y') is equivalent to ``del x.y''
6     """
delattr(x, y)

 

 1 class Foo(object):
 2  
 3     def __init__(self):
 4         self.name = 'wupeiqi'
 5  
 6     def func(self):
 7         return 'func'
 8  
 9 obj = Foo()
10  
11 # #### 检查是否含有成员 ####
12 hasattr(obj, 'name')
13 hasattr(obj, 'func')
14  
15 # #### 获取成员 ####
16 getattr(obj, 'name')
17 getattr(obj, 'func')
18  
19 # #### 设置成员 ####
20 setattr(obj, 'age', 18)
21 setattr(obj, 'show', lambda num: num + 1)
22  
23 # #### 删除成员 ####
24 delattr(obj, 'name')
25 delattr(obj, 'func')
反射代码示范

 

异常处理

1. 异常基础

在编程过程中为了增加友好性,在程序出现 bug 时一般不会将错误信息显示给用户,而是显示一个提示的页面,通俗来说就是不让用户看见大红页。

1 try:
2     pass
3 except Exception as ex:
4     pass

 

需求:将用户输入的两个数字相加

 1 while True:
 2     num1 = raw_input('num1:')
 3     num2 = raw_input('num2:')
 4     try:
 5         num1 = int(num1)
 6         num2 = int(num2)
 7         result = num1 + num2
 8     except Exception as e:
 9         print '出现异常,信息如下:'
10         print (e)

 

2. 异常种类

python中的异常种类非常多,每个异常专门用于处理某一项异常

 1 AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
 2 IOError 输入/输出异常;基本上是无法打开文件
 3 ImportError 无法引入模块或包;基本上是路径问题或名称错误
 4 IndentationError 语法错误(的子类) ;代码没有正确对齐
 5 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
 6 KeyError 试图访问字典里不存在的键
 7 KeyboardInterrupt Ctrl+C被按下
 8 NameError 使用一个还未被赋予对象的变量
 9 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
10 TypeError 传入对象类型与要求的不符合
11 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
12 导致你以为正在访问它
13 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
常见异常

 

 1 ArithmeticError
 2 AssertionError
 3 AttributeError
 4 BaseException
 5 BufferError
 6 BytesWarning
 7 DeprecationWarning
 8 EnvironmentError
 9 EOFError
10 Exception
11 FloatingPointError
12 FutureWarning
13 GeneratorExit
14 ImportError
15 ImportWarning
16 IndentationError
17 IndexError
18 IOError
19 KeyboardInterrupt
20 KeyError
21 LookupError
22 MemoryError
23 NameError
24 NotImplementedError
25 OSError
26 OverflowError
27 PendingDeprecationWarning
28 ReferenceError
29 RuntimeError
30 RuntimeWarning
31 StandardError
32 StopIteration
33 SyntaxError
34 SyntaxWarning
35 SystemError
36 SystemExit
37 TabError
38 TypeError
39 UnboundLocalError
40 UnicodeDecodeError
41 UnicodeEncodeError
42 UnicodeError
43 UnicodeTranslateError
44 UnicodeWarning
45 UserWarning
46 ValueError
47 Warning
48 ZeroDivisionError
其它异常

 

1 dic = ["wupeiqi", 'alex']
2 try:
3     dic[10]
4 except IndexError as e:
5     print (e)
实例:IndexError

 

1 dic = {'k1':'v1'}
2 try:
3     dic['k20']
4 except KeyError as e:
5     print (e)
实例:KeyError

 

1 s1 = 'hello'
2 try:
3     int(s1)
4 except ValueError as e:
5     print (e)
实例:ValueError

 

 

 

对于上述实例,异常类只能用来处理指定的异常情况,如果非指定异常则无法处理。

1 # 未捕获到异常,程序直接报错
2  
3 s1 = 'hello'
4 try:
5     int(s1)
6 except IndexError as e:
7     print (e)

 

所以,写程序时需要考虑到 try 代码块中可能出现的任意异常,可以这样写:

1 s1 = 'hello'
2 try:
3     int(s1)
4 except IndexError as e:
5     print (e)
6 except KeyError as e:
7     print (e)
8 except ValueError as e:
9     print (e)

 

万能异常  在python 的异常中,有一个万能异常:Exception,它可以捕获任何异常,即

1 s1 = 'hello'
2 try:
3     int(s1)
4 except Exception as e:
5     print (e)

 

问:既然有了万能异常,其它异常是不是就可以忽略了?

答:当然不是,对于特殊处理或提醒的异常需要先定义,最后定义 Exception 来确保程序正常运行。

1 s1 = 'hello'
2 try:
3     int(s1)
4 except KeyError as e:
5     print ('键错误')
6 except IndexError as e:
7     print ('索引错误')
8 except Exception as e:
9     print ('错误')

 

3. 其它异常结构

 1 try:
 2     # 主代码块
 3     pass
 4 except KeyError as e:
 5     # 异常时,执行该块
 6     pass
 7 else:
 8     # 主代码块执行完,执行该块
 9     pass
10 finally:
11     # 无论异常与否,最终执行该块
12     pass

 

4.  主动触发异常

1 try:
2     raise Exception('错误了。。。')
3 except Exception as e:
4     print (e)

 

5. 自定义异常

 1 class liuminException(Exception):
 2     def __init__(self,msg):
 3         self.message = msg
 4 
 5     def __str__(self):
 6         return self.message
 7 
 8 try:
 9     raise liuminException("我的异常:数据库连不上")
10 except liuminException as e:
11     print(e)

 

6. 断言

1 # assert 条件
2  
3 assert 1 == 1
4  
5 assert 1 == 2