Python:面向对象(二)
-
扩展:
继承:
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): pass d1 = D() d1.f() #打印结果:C #执行顺序:D-C-B-A-A-C
执行顺序图:
练习:
注:一定记住从底层开始找
####实例:找源码的过程(self.xxx(),从底层开始找)#### import socketserver r = 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() # 用类调用类方法,默认参数为cls Provice.f2() # 用类调用静态方法,默认无参数 #打印结果: show F1 F2
属性
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 #打印结果: start end
设置字段:字段是能获取也能重新设置:
class Foo: def __init__(self,name): self.name = name @property def end(self): temp = "%s A" % self.name return temp obj = Foo("num") #获取字段 print(obj.name) #设置字段 obj.name = "123" print(obj.name) #打印结果: num 123
设置属性:属性是将方法伪造成字段(调用时不加括号):@方法名.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 A 123 A
二、成员修饰符
-
公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能访问
私有成员和公有成员的定义不同:私有成员命名时,前两个字符是下划线。(特殊成员除外:__init__、__call__、__dict__等)
class C: def __init__(self): self.name = '公有字段' self.__foo = "私有字段"
私有成员和公有成员的访问限制不同:
静态字段
公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
私有静态字段:仅类内部可以访问
class C: name = "公有静态字段" def func(self): print C.name class D(C): def show(self): print C.name C.name # 类访问 obj = C() obj.func() # 类内部可以访问 obj_son = D() obj_son.show() # 派生类中可以访问
class C: __name = "公有静态字段" def func(self): print C.__name class D(C): def show(self): print C.__name C.__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() #打印结果: xo xx
普通字段
公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
私有普通字段:仅类内部可以访问
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() #打印结果: xx name
三、面向对象中一些常用特殊方法
1、__init__:构造方法,通过类创建对象时,自动触发执行。
class Foo: def __init__(self, name): self.name = name self.age = 18 obj = 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': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__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 pass 3 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没有属性x IOError 输入/输出异常;基本上是无法打开文件 ImportError 无法引入模块或包;基本上是路径问题或名称错误 IndentationError 语法错误(的子类) ;代码没有正确对齐 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 KeyboardInterrupt Ctrl+C被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) TypeError 传入对象类型与要求的不符合 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError
实例: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: # 主代码块 pass except KeyError as e: # 主代码异常时,执行该块 pass else: # 主代码块正常时,执行完,执行该块 pass finally: # 无论异常与否,最终执行该块 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) # 执行property obj.pp = 999 # 执行setter del 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.test print(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 ABCMeta from abc import abstractmethod class 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):pass obj = Foo()