-
扩展:
继承:
class A: def f(self): self.f1()class B(A): def f1(self): print("B")class C: def f1(self): print("C")class D(C,B): passd1 = D()d1.f()#打印结果:C#执行顺序:D-C-B-A-A-C
执行顺序图:
练习:
注:一定记住从底层开始找
####实例:找源码的过程(self.xxx(),从底层开始找)####import socketserverr = socketserver.ThreadingTCPServer()r.serve_forever()
执行父类的构造方式:
类名加括号执行类的普通字段:__init__
推荐:super(当前类,self).__init__()
不推荐:父类.__init__(self)
class Animal: def __init__(self): print("A构造方法") self.ty = "动物"class Cat(Animal): def __init__(self): print("B构造方法") self.n = "猫" super(Cat,self).__init__()c = Cat()print(c.n)#打印结果:B构造方法A构造方法猫##########c = Cat()print(c.__dict__)#打印结果:B构造方法A构造方法{ 'ty': '动物', 'n': '猫'}
-
面向对象的进阶
一、成员
类的成员可以分为三大类:字段、方法、属性
字段
字段包括普通字段和静态字段,两者在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同。
- 静态字段属于类
- 普通字段属于对象
注:静态字段:由类调用
普通字段:由对象调用
class Foo: # 静态字段,在类里(没有self),由类调用 country = "中国" # 普通字段,封装在对象中,由对象调用 def __init__(self,name): self.name = name # 普通方法,在类里,由对象调用 def show(self): print("show")print(Foo.country)obj = Foo("北京")print(obj.name)obj.show()#打印结果:中国北京show
应用场景:通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段。
方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,但是调用方式有所不同。
注:
普通方法:由对象调用,至少一个self参数,执行普通方法时,自动将调用该方法的对象赋值给self
静态方法:由类调用,没有默认参数,可以添加参数。关键字:@staticmethod
类方法:由类调用,至少一个cls参数,执行类方法时,自动将调用该方法的类名复制给cls。关键字:@classmethod
class Provice: # 静态字段 country = "China" # 普通字段 def __init__(self): self.name = "China" # 类方法 @classmethod def f1(cls): print("F1") # 静态方法 @staticmethod def f2(): print("F2") # 普通方法 def show(self): print("show") obj = Provice()obj.show() # 用对象调用普通方法Provice.f1() # 用类调用类方法,默认参数为clsProvice.f2() # 用类调用静态方法,默认无参数#打印结果:showF1F2
属性
Python中的属性其实是普通方法的变种。
属性的基本使用:
定义及调用:
定义:仅有一个self参数,加参数会报错。关键字:@property
调用:用对象调用属性,调用时不加括号
class Foo: def __init__(self): self.name = "num" def start(self): print("start") #定义属性 @property def end(self): print("end")obj = Foo()obj.start()obj.end#打印结果:startend
设置字段:字段是能获取也能重新设置:
class Foo: def __init__(self,name): self.name = name @property def end(self): temp = "%s A" % self.name return tempobj = Foo("num")#获取字段print(obj.name)#设置字段obj.name = "123"print(obj.name)#打印结果:num123
设置属性:属性是将方法伪造成字段(调用时不加括号):@方法名.setter
class Foo: def __init__(self,name): self.name = name @property def end(self): temp = "%s A" % self.name return temp @end.setter def end(self,value): print(value)obj = Foo("num")print(obj.end) #获取end值obj.name = "123" #设置end值,执行@方法名.setter方法print(obj.end) #打印结果:num A123 A
二、成员修饰符
-
公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能访问
私有成员和公有成员的定义不同:私有成员命名时,前两个字符是下划线。(特殊成员除外:__init__、__call__、__dict__等)
class C: def __init__(self): self.name = '公有字段' self.__foo = "私有字段"
私有成员和公有成员的访问限制不同:
静态字段
公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
私有静态字段:仅类内部可以访问
class C: name = "公有静态字段" def func(self): print C.nameclass D(C): def show(self): print C.nameC.name # 类访问obj = C()obj.func() # 类内部可以访问obj_son = D()obj_son.show() # 派生类中可以访问
class C: __name = "公有静态字段" def func(self): print C.__nameclass D(C): def show(self): print C.__nameC.__name # 类访问 ==> 错误obj = C()obj.func() # 类内部可以访问 ==> 正确obj_son = D()obj_son.show() # 派生类中可以访问 ==> 错误
class Foo: xo = "xo" __xx = "xx" def __init__(self): pass def f(self): print(Foo.__xx) def d(self): print("d")print(Foo.xo)# print(Foo.__xx)#报错,有__只能内部执行obj = Foo()obj.f()#打印结果:xoxx
普通字段
公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
私有普通字段:仅类内部可以访问
ps:如果想要强制访问私有字段,可以通过 【对象._类名__私有字段明 】访问(如:obj._C__foo),不建议强制访问私有成员。
class C: def __init__(self): self.foo = "公有字段" def func(self): print self.foo # 类内部访问class D(C): def show(self): print self.foo # 派生类中访问obj = C()obj.foo # 通过对象访问obj.func() # 类内部访问obj_son = D();obj_son.show() # 派生类中访问
class C: def __init__(self): self.__foo = "私有字段" def func(self): print self.foo # 类内部访问class D(C): def show(self): print self.foo # 派生类中访问obj = C()obj.__foo # 通过对象访问 ==> 错误obj.func() # 类内部访问 ==> 正确obj_son = D();obj_son.show() # 派生类中访问 ==> 错误
class Foo: xo = "xo" __xx = "xx" def __init__(self): self.__name = "name" def f(self): print(Foo.__xx) def d(self): print(self.__name)obj = Foo()obj.f()obj.d()#打印结果:xxname
三、面向对象中一些常用特殊方法
1、__init__:构造方法,通过类创建对象时,自动触发执行。
class Foo: def __init__(self, name): self.name = name self.age = 18obj = Foo('wupeiqi') # 自动执行类中的 __init__ 方法
2、__del__:析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self): pass
3、__call__:对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print '__call__'obj = Foo() # 执行 __init__obj() # 执行 __call__
4、__getitem__、__setitem__、__delitem__:获取、设置、删除数据。用于索引操作,如字典。
class Foo(object): def __getitem__(self, key): print '__getitem__',key def __setitem__(self, key, value): print '__setitem__',key,value def __delitem__(self, key): print '__delitem__',key obj = Foo() result = obj['k1'] # 自动触发执行 __getitem__obj['k2'] = '123' # 自动触发执行 __setitem__del obj['k1'] # 自动触发执行 __delitem__
5、__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__# 输出:{'country': 'China', '__module__': '__main__', 'func':, '__init__': , '__doc__': None}obj1 = Province('HeBei',10000)print obj1.__dict__# 获取 对象obj1 的成员# 输出:{'count': 10000, 'name': 'HeBei'}obj2 = Province('HeNan', 3888)print obj2.__dict__# 获取 对象obj1 的成员# 输出:{'count': 3888, 'name': 'HeNan'}
6、__iter__:用于迭代器,之所以列表、字典、元祖可以进行for循环,是因为类型内部定义了__iter__。
class Foo(object): def __init__(self, sq): self.sq = sq def __iter__(self): return iter(self.sq)obj = Foo([11,22,33,44])for i in obj: print i
7、__str__:如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
class Foo: def __str__(self): return '123'obj = Foo()print obj# 输出:123
-
异常处理
1、异常基础
程序出现bug时,一般不会将错误信息显示给用户,而是实现一个提示的页面。
异常处理格式:
1 try:2 pass3 except Exception as e:4 pass
练习:将用户输入的两个数字相加
while True: num1 = input("num1:") num2 = input("num2:") try: num1 = int(num1) num2 = int(num2) result = num1 + num2 except Exception as e: print("出现异常,信息如下:") print(e)
2、异常种类
异常种类,不同种类处理不同异常
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性xIOError 输入/输出异常;基本上是无法打开文件ImportError 无法引入模块或包;基本上是路径问题或名称错误IndentationError 语法错误(的子类) ;代码没有正确对齐IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]KeyError 试图访问字典里不存在的键KeyboardInterrupt Ctrl+C被按下NameError 使用一个还未被赋予对象的变量SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)TypeError 传入对象类型与要求的不符合UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它ValueError 传入一个调用者不期望的值,即使值的类型是正确的
ArithmeticErrorAssertionErrorAttributeErrorBaseExceptionBufferErrorBytesWarningDeprecationWarningEnvironmentErrorEOFErrorExceptionFloatingPointErrorFutureWarningGeneratorExitImportErrorImportWarningIndentationErrorIndexErrorIOErrorKeyboardInterruptKeyErrorLookupErrorMemoryErrorNameErrorNotImplementedErrorOSErrorOverflowErrorPendingDeprecationWarningReferenceErrorRuntimeErrorRuntimeWarningStandardErrorStopIterationSyntaxErrorSyntaxWarningSystemErrorSystemExitTabErrorTypeErrorUnboundLocalErrorUnicodeDecodeErrorUnicodeEncodeErrorUnicodeErrorUnicodeTranslateErrorUnicodeWarningUserWarningValueErrorWarningZeroDivisionError
实例:IndexError、KeyError、ValueError
########li = []try: li[3]except IndexError as e: print(e)########dic = { "k1":"v1"}try: dic["k2"]except KeyError as e: print(e)########s = "hello"try: int(s)except ValueError as e: print(e)#打印结果:list index out of range'k2'invalid literal for int() with base 10: 'hello'
异常类只能用来处理制定的异常情况,如果非制定异常则无法处理
#未捕获到异常,程序直接报错s = "hello"try: int(s)except ValueError as e: print(e)
为了避免有其他异常,可以这样写:
s = "hello"try: int(s)except IndexError as e: print(e)except KeyError as e: print(e)except ValueError as e: print(e)
万能异常:因为异常不止几种,有一个万能异常,就可以捕获任意异常
s = "hello"try: int(s)except Exception as e: print(e)
虽然有万能异常,但是也不能只用万能异常就可以来了,因为对于特殊处理或提醒的异常需要先定义,最后再定义Exception来确保程序正常运行。
s = "hello"try: # li = [] int(s)except IndexError as e: print(e)except ValueError as e: print(e)except Exception as e: print("123")
3、异常其他结构
分两种情况:
a:try --> exceot KeyError as e --> finally
b: try --> else --> finally
try: # 主代码块 passexcept KeyError as e: # 主代码异常时,执行该块 passelse: # 主代码块正常时,执行完,执行该块 passfinally: # 无论异常与否,最终执行该块 pass
4、主动触发异常
try: print("123") raise Exception("出错了...")except Exception as e: print(e)#打印结果:123出错了...
5、断言
#assert 作为关键字#条件成立就执行assert 1 == 1 #条件不成立就报错assert 1 == 2
作业:选课(对象中可以封装对象)
特性补充:
方法一:装饰器+方法
class Foo: def func(self): print("func") @property def pp(self): return 123 @pp.setter def pp(self,value): print(value) @pp.deleter def pp(self): print("del")obj = Foo()print(obj.pp) # 执行propertyobj.pp = 999 # 执行setterdel obj.pp # 执行 deletser
方法二:property+方法
class Foo: def f1(self): return "123" def f2(self,value): print(value) def f3(self): print("del") test = property(fget=f1,fset=f2,fdel=f3)obj = Foo()ret = obj.testprint(ret)obj.test = "456"del obj.test
Python2.7继承流程
经典类(当未继承object时):
#深度优先class Bar: def f1(self): print("BAR")class A(Bar): def f(self): print("A")class B(Bar): def f1(self): print("B")class C(A): def f(self): print("C")class D(B): def f1(self): print("D")class E(C,D): def f(self): print("E")obj = E()obj.f1()#E-C-A-Bar
流程图:
新式类(继承object时):
#广度优先class Bar(object): def f1(self): print("BAR")class A(Bar): def f(self): print("A")class B(Bar): def f(self): print("B")class C(A): def f(self): print("C")class D(B): def f(self): print("D")class E(C,D): def f(self): print("E")obj = E()obj.f1()#执行流程:E-C-A-D-B-Bar
流程图:
抽象方法抽象类:约束
#####接口(python里面没有接口)##########用抽象类加抽象方法实现接口#####子类继承抽象类必须至少实现抽象方法的方法==子类中的方法必须等于大于基类中的方法from abc import ABCMetafrom abc import abstractmethodclass Father(metaclass=ABCMeta): @abstractmethod def f1(self):pass @abstractmethod def f2(self):pass#####继承抽象类和抽象方法class Foo(Father): def f1(self):pass def f2(self):pass def f3(self):passobj = Foo()