图解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
for循环的内部实现

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__

对 intstr 等内置数据类型排序时,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
View Code

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
实例:IndexError
dic = {'k1':'v1'}
try:
    dic['k20']
except KeyError, e:
    print e
实例:KeyError
s1 = 'hello'
try:
    int(s1)
except ValueError, e:
    print e
实例:ValueError

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

 
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
View Code

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

s1 = 'hello'
try:
    int(s1)
except Exception,e:
    print e
View Code

接下来你可能要问了,既然有这个万能异常,其他异常是不是就可以忽略了!

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

s1 = 'hello'
try:
    int(s1)
except KeyError,e:
    print '键错误'
except IndexError,e:
    print '索引错误'
except Exception, e:
    print '错误'
View Code

3、异常其他结构

try:
    # 主代码块
    pass
except KeyError,e:
    # 异常时,执行该块
    pass
else:
    # 主代码块执行完,执行该块
    pass
finally:
    # 无论异常与否,最终执行该块
    pass
View Code

4、主动触发异常

try:
    raise Exception('错误了。。。')
except Exception,e:
    print e
View Code

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
View Code

6、断言

# assert 条件
 
assert 1 == 1
 
assert 1 == 2
View Code

四、反射

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')
View Code
反射
    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
View Code

 

 

 


四.销毁对象(垃圾收集)   --补充知识

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>
Python

通常情况下,垃圾回收器会销毁孤立的实例并回收其空间。 但是,类可以实现调用析构函数的特殊方法__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

 

posted @ 2017-09-25 10:22  江湖一浪子  阅读(940)  评论(0编辑  收藏  举报