图解Python 【第六篇】:面向对象-类-进阶篇
本节内容一览图:
一、类成员修饰符
每一个类的成员都有两种形式:
- 公有成员,在任何地方都能访问
- 私有成员,只能在类的内部才能访问
1.1、私有成员和公有成员的定义不同:
私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)
前面提到过 如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__
,在Python中,实例的变量名如果以__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问
例如:
class C: def __init__(self): self.name = '公有字段' self.__foo = "私有字段"
1.2、私有成员和公有成员的访问限制不同
静态字段:
- 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
- 私有静态字段:仅类内部可以访问;
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() # 派生类中可以访问 ==> 错误
普通字段:
- 公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
- 私有普通字段:仅类内部可以访问;
ps:如果想要强制访问私有字段,可以通过 【对象._类名__私有字段明 】访问(如:obj._C__foo),不建议强制访问私有成员。
# 公有普通字段 class Foo: def __init__(self, name): self.name = name def f1(self): return self.name obj = Foo("alex") print(obj.name) print(obj.f1()) # 私有普通字段 class Foo: def __init__(self, name): self.__name = name def f1(self): return self.__name obj = Foo("alex") print(obj.f1()) print(obj.__name) # 继承的私有普通字段 class Foo: def __init__(self, name): self.__name = name def f1(self): return self.__name class Bar(Foo): def f2(self): return self.__name obj = Bar("alex") print(obj.f1()) print(obj.__name) # 公有的静态字段 class Foo: cc = '123' def __init__(self, name): self.__name = name def f1(self): return self.__name print(Foo.cc) # 私有的静态字段 class Foo: __cc = '123' def __init__(self, name): self.__name = name @staticmethod def f1(): return Foo.__cc print(Foo.f1()) print(Foo.__cc) # 如何访问私有普通字段? class Foo: def __init__(self, name): self.__name = name def f1(self): return self.__name obj = Foo("alex") # print(obj.f1()) print(obj._Foo__name)
方法、属性的访问于上述方式相似,即:私有成员只能在类内部使用
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() # 派生类中访问 ==> 错误
ps:非要访问私有属性的话,可以通过 对象._类__属性名
注:关于静态字段和普通字段的解释可以在上一节查找
补充:访问限制
Python对属性权限的控制是通过属性名来实现的,如果一个属性由双下划线开头(__),该属性就无法被外部访问
如果一个属性以"__xxx__"的形式定义,那它又可以被外部访问了,以"__xxx__"定义的属性在Python的类中被称为特殊属性,有很多预定义的特殊属性可以使用,通常我们不要把普通属性用"__xxx__"定义。
以单下划线开头的属性"_xxx"虽然也可以被外部访问,但是,按照习惯,他们不应该被外部访问。
二、类的特殊方法
上文介绍了Python的类成员以及成员修饰符,从而了解到类中有字段、方法和属性三大 类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。无论人或事物往往都有不按套路出牌的情况,Python的类 成员也是如此,存在着一些具有特殊含义的成员,详情如下:
注意:下文的__都是双下划线
1、__doc__
功能:表示类的描述信息
class Foo: """描述类的信息""" def __init__(self, name): self.name = name def show(self): print(self.name) obj = Foo.__dict__
2、__module__和__class__
功能:
__module__ 表示当前操作的对象在哪个模块
__class__ 表示当前操作的对象的类是什么
# lib/aa.py #!/usr/bin/env python # -*- coding:utf-8 -*- class C: def __init__(self): self.name = 'wupeiqi' # ################################ from lib.aa import C obj = C() print obj.__module__ # 输出 lib.aa,即:输出模块 print obj.__class__ # 输出 lib.aa.C,即:输出类
3、__init__
功能:构造方法,通过类创建对象时,自动触发执行
对象实例化的过程
创建类的对象---------------------->初始化对象
def __new__(cls) def __init__(self)
注意:__new__(cls)有一个返回值
class Foo: def __init__(self, name): self.name = name self.age = 18 obj = Foo('sfs') # 自动执行类中的 __init__ 方法
4、__del__
功能:析构方法,当对象在内存中被释放时,自动触发执行
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self): pass
5、__call__
功能:对象后面加括号,触发执行
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print '__call__' obj = Foo() # 执行 __init__ obj() # 执行 __call__
6、__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'}
7、__str__和__repr__
功能:如果一个类中定义了__str__方法,那么在打印对象时,默认输出改方法的返回值
class Foo: def __str__(self): return 'wupeiqi' obj = Foo() print obj # 输出:wupeiqi
补充:
Python 定义了__str__()和__repr__()两种方法,__str__()用于显示给用户,而__repr__()用于显示给开发人员。
8、__getitem__、__setitem__、__delitem__
功能:用于索引操作,如字典。以上分别表示获取、设置、删除数据
#!/usr/bin/env python # -*- coding:utf-8 -*- 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'] = 'wupeiqi' # 自动触发执行 __setitem__ del obj['k1'] # 自动触发执行 __delitem__
9、__getslice__、__setslice__、__delslice__(Python 2.x中的方法,Python 3.X中使用__getitem__、__setitem__、__delitem__实现类似的功能)
功能:该方法用于分片操作,如:列表
#!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __getslice__(self, i, j): print '__getslice__',i,j def __setslice__(self, i, j, sequence): print '__setslice__',i,j def __delslice__(self, i, j): print '__delslice__',i,j obj = Foo() obj[-1:1] # 自动触发执行 __getslice__ obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__ del obj[0:2] # 自动触发执行 __delslice__
10、__iter__
功能:用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__
class Foo(object): pass obj = Foo() for i in obj: print i # 报错:TypeError: 'Foo' object is not iterable
#!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __iter__(self): pass obj = Foo() for i in obj: print i # 报错:TypeError: iter() returned non-iterator of type 'NoneType'
#!/usr/bin/env python # -*- coding:utf-8 -*- 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
从以上的执行内容可以看出,for循环迭代的其实是 iter([11,22,33,44]) ,所以执行流程可以变更为:
#!/usr/bin/env python # -*- coding:utf-8 -*- obj = iter([11,22,33,44]) for i in obj: print i
#!/usr/bin/env python # -*- coding:utf-8 -*- obj = iter([11,22,33,44]) while True: val = obj.next() print val
11、__new__和__metaclass__
阅读如下代码:
class Foo(object): def __init__(self): pass obj = Foo() # obj是通过Foo类实例化的对象
上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。
print type(obj) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 print type(Foo) # 输出:<type 'type'> 表示,Foo类对象由 type 类创建
所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
那么,创建类就可以有两种方式:
方法1:普通方式
class Foo(object): def func(self): print 'hello wupeiqi'
方法2:特殊方式(type类的构造函数)
def func(self): print 'hello wupeiqi' Foo = type('Foo',(object,), {'func': func}) #type第一个参数:类名 #type第二个参数:当前类的基类 #type第三个参数:类的成员
-->类是由type类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
补充:
12.__cmp__
对 int、str 等内置数据类型排序时,Python的 sorted() 按照默认的比较函数 cmp 排序,但是,如果对一组 Student 类的实例排序时,就必须提供我们自己的特殊方法 __cmp__():
13.__len__
如果一个类表现得像一个list,要获取有多少个元素,就得用 len() 函数
三、异常处理
1、异常基础
在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面,通俗来说就是不让用户看见大黄页!!!
1
2
3
4
|
try : pass except Exception,ex: pass |
需求:将用户输入的两个数字相加
while True: num1 = raw_input('num1:') num2 = raw_input('num2:') try: num1 = int(num1) num2 = int(num2) result = num1 + num2 except Exception, e: print '出现异常,信息如下:' print e
2、异常种类
python中的异常种类非常多,每个异常专门用于处理某一项异常!!!
1、name Error 变量没定义
2、Syntax Error 语法错误
3、IO Error 打开一个不存在的目录或文件
4.Zero DivisionError 10/0 除零错误,即零不可以当做分母】
5.a=int('dd') :Value Error 传入值无效
6.keyboard Interrupt 强制终止程序产生
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
dic = ["wupeiqi", 'alex'] try: dic[10] except IndexError, e: print e
dic = {'k1':'v1'} try: dic['k20'] except KeyError, e: print e
s1 = 'hello' try: int(s1) except ValueError, e: print e
对于上述实例,异常类只能用来处理指定的异常情况,如果非指定异常则无法处理。
1
2
3
4
5
6
7
|
# 未捕获到异常,程序直接报错 s1 = 'hello' try : int (s1) except IndexError,e: print e |
所以,写程序时需要考虑到try代码块中可能出现的任意异常,可以这样写:
s1 = 'hello' try: int(s1) except IndexError,e: print e except KeyError,e: print e except ValueError,e: print e
万能异常 在python的异常中,有一个万能异常:Exception,他可以捕获任意异常,即:
s1 = 'hello' try: int(s1) except Exception,e: print e
接下来你可能要问了,既然有这个万能异常,其他异常是不是就可以忽略了!
答:当然不是,对于特殊处理或提醒的异常需要先定义,最后定义Exception来确保程序正常运行。
s1 = 'hello' try: int(s1) except KeyError,e: print '键错误' except IndexError,e: print '索引错误' except Exception, e: print '错误'
3、异常其他结构
try: # 主代码块 pass except KeyError,e: # 异常时,执行该块 pass else: # 主代码块执行完,执行该块 pass finally: # 无论异常与否,最终执行该块 pass
4、主动触发异常
try: raise Exception('错误了。。。') except Exception,e: print e
5、自定义异常
class WupeiqiException(Exception): def __init__(self, msg): self.message = msg def __str__(self): return self.message try: raise WupeiqiException('我的异常') except WupeiqiException,e: print e
6、断言
# assert 条件 assert 1 == 1 assert 1 == 2
四、反射
python中的反射功能是由以下四个内置函数提供:hasattr、getattr、setattr、delattr,改四个函数分别用于对对象内部执行:检查是否含有某成员、获取成员、设置成员、删除成员。
class Foo(object): def __init__(self): self.name = 'wupeiqi' def func(self): return 'func' obj = Foo() # #### 检查是否含有成员 #### hasattr(obj, 'name') hasattr(obj, 'func') # #### 获取成员 #### getattr(obj, 'name') getattr(obj, 'func') # #### 设置成员 #### setattr(obj, 'age', 18) setattr(obj, 'show', lambda num: num + 1) # #### 删除成员 #### delattr(obj, 'name') delattr(obj, 'func')
反射 getattr(obj,str) #### 获取成员 #### setattr(obj,str,val) #### 设置成员 #### hasattr(obj,str) ### 检查是否含有成员 #### delattr(obj,str) #### 删除成员 ####
结论:反射是通过字符串的形式操作对象相关的成员。一切事物都是对象!!!
异常处理 try: # 主代码块 pass except KeyError as e: # 异常时,执行该块 pass else: # 主代码块执行完,执行该块 pass finally: # 无论异常与否,最终执行该块 pass ################################################ try ... except (ValueError,KeyError),e #in 2.7写法 except (ValueError,KeyError) as e : 3.x写法 except Exception as e:放在异常处理最后面, else: #没发生异常,就执行 finally, 无论如何都执行 raise ValueError
四.销毁对象(垃圾收集) --补充知识
Python自动删除不需要的对象(内置类型或类实例)以释放内存空间。 Python定期回收不再使用的内存块的过程称为垃圾收集。
Python的垃圾收集器在程序执行期间运行,当对象的引用计数达到零时触发。 对象的引用计数随着指向它的别名数量而变化。
当对象的引用计数被分配一个新名称或放置在容器(列表,元组或字典)中时,它的引用计数会增加。 当用del
删除对象的引用计数时,引用计数减少,其引用被重新分配,或者其引用超出范围。 当对象的引用计数达到零时,Python会自动收集它。
a = 40 # Create object <40>
b = a # Increase ref. count of <40>
c = [b] # Increase ref. count of <40>
del a # Decrease ref. count of <40>
b = 100 # Decrease ref. count of <40>
c[0] = -1 # Decrease ref. count of <40>
通常情况下,垃圾回收器会销毁孤立的实例并回收其空间。 但是,类可以实现调用析构函数的特殊方法__del__()
,该方法在实例即将被销毁时被调用。 此方法可能用于清理实例使用的任何非内存资源。
五.类和运算符
1.利用魔术方法可以使运算符被Python的标准语法支持
2.比较运算符
__cmp__(self,other) 比较
__eq__(self,other) 处理等于
__lt__(self,other) 小于
__gt__(self,other) 大于
3.数字运算符
__add__(self,other) 加
__sub__(self,other) 减
__mul__(self,other) 乘
__div__(self,other) 除
4.逻辑运算符
__or__(self,other) 或
__and__(self,other) 和
声明:
本人在学习老男孩python自动化网络课程后,结合所学整理做次笔记,本文内容多出
Alex老师博客:http://www.cnblogs.com/alex3714/articles/5740985.html
武沛齐老师博客:http://www.cnblogs.com/wupeiqi/articles/5453708.html
感谢老男孩教育老师Alex,武沛齐老师,本文多从二位老师文章中结合整理
http://www.cnblogs.com/wupeiqi/p/4766801.html
http://www.yiibai.com/python/python_classes_objects.html