面向对象高级-day8
内容概要
- 上节回顾
- 对象的内置方法
- 源类
- 异常处理
- 编写一个FTP程序(面向对象的应用)
一、上节回顾
1.1.静态方法和类方法(区别)
- 静态方法和本身并没有什么关系,只是在调用的时候需要加上类名
- 类方法可以方法类变量,不可以调用实例变量
- 其实类方法和静态方法在实际的运用中并不是很多见
class Foo(object): def __init__(self,name): self.name = name @staticmethod def py_print(self): print("this is you're %name"%self.name) f = Foo('LeonYan') f.py_print() #错误的使用类的属性 f.py_print(f) #静态方法调用类方法需要有几个将参数传入进去,换而言之就是静态方法在调用的时候不需要传参数
1.2.属性方法——property(把一个类方法变成一个类属性)
在调用的时候不用加括号
触发方法(set)
- function.setter在属性被赋值的时候被自动执行
- 在使用setter方法的时候必须要一个一个位置参数来接收属性被赋予的值
正确的姿势
class Foo(object): name = "CHEN" def __init__(self,name): self.name = name @property def py_print(self): print("this is you're name : %s"%self.name) @py_print.setter def py_print(self,age): print("LeonYan was %s years old"%age) f = Foo('LeonYan') f.py_print = "20"
二、内置方法
2.1:__doc__方法
class Foo(object): """这个是一个空的类""" def func(self): pass print(Foo.__doc__)
2.2 : __module__ 和__class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
from lib.aa import C obj = C() print obj.__module__ # 输出 lib.aa,即:输出模块 print obj.__class__ # 输出 lib.aa.C,即:输出类
2.3 __call__
__call__:方法在类被实例化后,直接调用对象执行__call__方法
class Foo(object): def dd(self): pass def __call__(self, *args, **kwargs): print("running function call") f = Foo() f() 打印running function call
三、源类
_new_ 和 __metaclass__(类的创建过程)
在介绍这个方法的时候我先写一段最简单的类
class Foo():
def __init__(self,name)
self.name = name
F = Foo("Leonyan")
在python中我们都听过一切皆对象这个概念,理解起来就是所有东西都可以用点的方式来调用。就拿上面的示例来说F就是有Foo实例化的所得到的对象,这一点没有任何问题。但是Foo这个类又是通过什么得来的呢?
print(type(f)) #---><class '__main__.Foo'> print(type(Foo)) #----><class 'type'>
我擦通过type看类和对象的类型我们发现Foo这个类竟然是有type创建的?type不是python中的内置函数吗?其实type是一切的根源,包括类也是它来创建的。但是如果你问type又是什么创建的,我只能说你没有灵性了,因为你这就是问了一个鸡生蛋的故事了。
下面我们用type创建一个类:
def leonyan(food): print("I am eta %s"%food) Test = type("Test",(object,),{"leonyan":leonyan}) print(Test) Test.leonyan("abc")
- leonyan是为了方便加入到类中的属性
- type(“Test”)是类名,为了方面理解直接将等号前面也命名为Test,方面在实例化后调用不迷惑
- (object):继承的类因为可以继承多个类多以是元祖格式
- {}:里面是这个类的属性和变量
我们再将这个方法实例化看一看
def __init__(self,name,age,like): self.name = name self.age = age self.like =like def leonyan(self,food): print("I am eta %s"%food) Test = type("Test",(object,),{"__init__":__init__,"leonyan":leonyan}) T = Test("leonyan",'18','girl') T.leonyan("rou")
实例化误区:定义类属性的时候需要加上一个self的位置变量,因为在实例化的时候我们调用实例点方法的方式的时候默认会把自己当做第一个参数传到这个方法当中。
__new__类最开始加载的方法,意思是在我们实例化类的时候并不是首先运行的__init__方法而是__new__方法,再通过new方法调用init方法:
class Leon(object): def __init__(self,age): self.age = age def __new__(cls, *args, **kwargs): print("ha ha __init__ function is not start") L = Leon("18") 输出的结果是ha ha __init__ function is not start
写到这里或许不太相信这个类没有运行init方法,这个时候我们做了一次测试:如果init方法被执行了的话我们在调用L.age的时候肯定是有对应的值得因为我们在实例化的时候传入了这个值,通过下面的示例就就可以看出在实例化的过程中根本就没有执行init方法。
new的用法:如果我们自己重新构造了new函数,但是还是要实现原来new方法的功能。
class Leon(object): def __init__(self,age): self.age = age def __new__(cls, *args, **kwargs): print("ha ha __init__ function is not start",*args,**kwargs) obj = object.__new__(cls) print("obj",obj) return obj L = Leon("18") print(L) print(L.age)
- 首先我们看到被实例化的L其实就是return的obj
-
object.__new__(cls)固定写法,cls是类本身
- return了上面的obj其实就执行的这个类原来的new函数
- 注意上面重新构造的obj其实和已经实例化后的L不是一个东西,虽然他们的内存地址相同,但是通过dir()测试我们会发现obj这个对象中并没有age这个属性
非常难理解的__metaclass__
到现在我们已经知道类是有type实例化产生,type类中如何实现创建类?类又是如何创建对象?类中有一个属性_-metaclass__,其用来表示该类由,谁来实例化创建,所以我们可以为__metaclass__设置type类的派生类,从而查看类创建的过程。
class MyType(type): def __init__(self,*args,**kwargs): print("MyType __init__下面:",*args,**kwargs) def __new__(cls, *args, **kwargs): print("MyType __new__下面:",*args,**kwargs) obj = type.__new__(cls,*args,**kwargs) return obj class Foo(object,metaclass=MyType): def __init__(self,name): self.name = name print("Foo __init__") f = Foo('Leonyan') print(f.name)
- 一个最基本的类是由new方法去调用init方法,而new又是有type来创建的
- Foo我们可以身为是几个基类,他继承了object和MyType也就是我们得源类
- MyType可以控制我们Foo的创建过程
- MyType(源类)也是从new方法开始执行,将结果返回给实例
在上面示例中Foo其实是MyType的实例也就是说为什么我们可以在Foo后面加上()括号的原因,具体可以使用type(Foo)测试。但是新的问题又来了,如果Foo就是一个实例,那为什么还可以加上括号去运行呢?这里可能会有人想到call方法,没错call方法可以一个实例加上括号去运行,而且MyType也封装了它,导致Foo可以执行。
class MyType(type): def __init__(self,*args,**kwargs): print("MyType __init__下面:",*args,**kwargs) def __call__(self, *args, **kwargs): print("MyType Call",*args,**kwargs) obj = self.__new__(self) obj.age = 22 self.__init__(obj,*args,**kwargs) return obj def __new__(cls, *args, **kwargs): print("MyType __new__下面:",*args,**kwargs) obj = type.__new__(cls,*args,**kwargs) return obj class Foo(object,metaclass=MyType): def __init__(self,name): self.name = name print("Foo __init__") # f = Foo('Leonyan') print(f.name,f.age)
四、异常处理
在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面,通俗来说就是不让用户看见大黄页!!!
4.1、异常种类
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x IOError 输入/输出异常;基本上是无法打开文件 ImportError 无法引入模块或包;基本上是路径问题或名称错误 IndentationError 语法错误(的子类) ;代码没有正确对齐 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 KeyboardInterrupt Ctrl+C被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) TypeError 传入对象类型与要求的不符合 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
dic = ["wupeiqi", 'alex'] try: dic[10] except IndexError, e: print e
4.2、主动触发异常
try: raise Exception('错误了。。。') except Exception,e: print e
4.3、自定义异常
class WupeiqiException(Exception): def __init__(self, msg): self.message = msg def __str__(self): return self.message try: raise WupeiqiException('我的异常') except WupeiqiException,e: print e
5.FTP程序:详见GitHub