CrazyWing:Python自动化运维开发实战 十8、Python面向对象

导语

在Python中,类和 OOP 都不是平常编程所必需的。尽管它从一开始设计就是面向对象的,而且结构上支持 OOP,但 Python 没有限定或要求你在你的应用中写 OOP 的代码。
不过!不学面向对象能够么?嘿嘿!那是必须的不能够!python

相关概念

• 类(Class):程序员

用来描述具备相同的属性和方法的对象的集合。它定义了该集合中每一个对象所共有的属性和方法。对象是类的实例。 编程

• 方法 :数据结构

类中定义的函数。python2.7

• 类变量 :ide

类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体以外。类变量一般不做为实例变量使用。函数

• 实例变量:oop

定义在方法中的变量,只做用于当前实例ui

• 数据成员:url

类变量或者实例变量,用于处理类及其实例对象的相关的数据。

• 方法重写:

若是从父类继承的方法不能知足子类的需求,能够对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。

• 继承 :

即一个派生类(derived class)继承基类(base class)的字段和方法。继承也容许把一个派生类的对象做为一个基类对象对待。例如,有这样一个设计 :一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例如,Dog是一个Animal)。

• 实例化 :

建立一个类的实例,类的具体对象。

• 对象 :

经过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

建立类

使用class语句来建立一个新类,class以后为类的名称并以冒号结尾

class ClassName:
   '类的帮助信息'     #类文档字符串,类的帮助信息能够经过ClassName.__doc__查看
   class_suite        #类体,class_suite 由类成员,方法,数据属性组成。

例:
#!/usr/bin/python
class Employee:
'全部员工的基类'
empCount = 0
def init(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1

def displayCount(self):
     print "Total Employee %d" % Employee.empCount

   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary

• empCount变量是一个类变量,它的值将在这个类的全部实例之间共享。你能够在内部类或外部类使用Employee.empCount访问。
init()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当建立了这个类的实例时就会调用该方法

建立实例对象

要建立一个类的实例,可使用类的名称,并经过init方法接受参数。
建立 Employee 类的第一个对象

emp1 = Employee("wing", 2000)

建立 Employee 类的第二个对象

emp2 = Employee("liuchao", 5000)

访问属性

使用点(.)来访问对象的属性:

emp1.displayEmployee()
    emp2.displayEmployee()
    print "Total Employee %d" % Employee.empCount

使用函数的方式来访问属性:
• getattr(obj, name[, default]) : 访问对象的属性。
• hasattr(obj,name) : 检查是否存在一个属性。
• setattr(obj,name,value) : 设置一个属性。若是属性不存在,会建立一个新属性。
• delattr(obj, name) : 删除属性。
例:

hasattr(emp1, 'age')      # 若是存在 'age' 属性返回 True。
    getattr(emp1, 'age')      # 返回 'age' 属性的值
    setattr(emp1, 'age', 8)  # 添加属性 'age' 值为 8
    delattr(emp1, 'age')      # 删除属性 'age'

完整实例:
#!/usr/bin/python
class Employee:
'全部员工的基类'
empCount = 0
def init(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1

def displayCount(self):
     print "Total Employee %d" % Employee.empCount

   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary

emp1 = Employee("Zara", 2000)
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount

输出结果:
Name :  Zara ,Salary:  2000
Name :  Manni ,Salary:  5000
Total Employee 2

添加、删除、修改类的属性:

emp1.age = 7  # 添加一个 'age' 属性
emp1.age = 8  # 修改 'age' 属性
del emp1.age   # 删除 'age' 属性

静态变量和实例变量

亦可称为静态数据(类数据属性,也叫静态字段,静态属性)。
这些数据是与它们所属的类对象绑定的,不依赖于任何类实例。
若是你熟悉 Java 或 C++,这种类型的数据至关于在一个变量声明前加上 static 关键字。
静态成员一般仅用来跟踪与类相关的值。
通常,咱们会考虑用实例属性(也叫普通变量或实例变量),而不是类属性。

静态变量在内存中只保存一份
实例变量在每一个对象中都保存一份

应用场景: 经过类建立对象时,若是每一个对象都具备相同的字段,那么就使用静态变量
例1:类数据属性(foo):

class C(object):
... foo = 100
print C.foo
100
C.foo = C.foo + 1
print C.foo
101

注意,上面的代码中,看不到任何类实例的引用

例2:

class Province:
        # 静态变量
        country = '中国'
        def __init__(self, name):
            # 实例变量
            self.name = name

    # 直接访问普通字段
    obj = Province('河北省')
    print obj.name
    #直接访问静态字段
    Province.country

类属性与方法

类的私有属性(私有字段)

__private_attrs:两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。

类的方法

在类地内部,使用def关键字能够为类定义一个方法,与通常函数定义不一样,类方法必须包含参数self,且为第一个参数

类的私有方法

__private_method:两个下划线开头,声明该方法为私有方法,不能在类地外部调用。在类的内部调用 self.__private_methods

实例

#!/usr/bin/python
class JustCounter:
    __secretCount = 0  # 私有变量
    publicCount = 0     # 公开变量

    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print self.__secretCount

counter = JustCounter()
counter.count()
counter.count()
print counter.publicCount
print counter.__secretCount   # 报错,实例不能访问私有变量Python 经过改变名称来包含类名:
1
2
2
Traceback (most recent call last):
  File "test.py", line 17, in <module>
    print counter.__secretCount     # 报错,实例不能访问私有变量
AttributeError: JustCounter instance has no attribute '__secretCount'

Python不容许实例化的类访问私有数据,但你可使用 object._className__attrName 访问属性,将以下代码替换以上代码的最后一行代码:
.........................
print counter._JustCounter__secretCount
执行结果:
1
2
2
2

特殊类属性(Python内置类属性):

C.__dict__ :类C的属性(包含一个字典,由类的数据属性组成) 
   C.__doc__ :类C的文档字符串 
   C.__name__:类C类名(字符串) 
   C.__module__:类C定义所在的模块(类的全名是'__main__.className',若是类位于一个导入
模块mymod中,那么className.__module__ 等于 mymod) 
   C.__bases__ : 类C的全部父类构成元素(包含了一个由全部父类组成的元组) 
   C.__class__:实例对应的类(仅新式类中)

根据上面定义的类 MyClass,有以下结果:
     >>> MyClass.__name__
    'MyClass'

    >>> MyClass.__doc__
    'MyClass class definition'

    >>> MyClass.__bases__
    (<type 'object'>,)

    >>> print MyClass.__dict__
    {'__doc__': None, 'myVersion': 1, 'showMyVersion':
    <function showMyVersion at 950ed0>, '__module__': '__main__'}

    >>> MyClass.__module__
    '__main__'

    >>> MyClass.__class__
    <type 'type'>

__name__
    是给定类的字符名字。
    它适用于那种只须要字符串(类对象的名字),而非类对象自己的状况。一些内建的类型也有这个属性,咱们将会用
到其中之一来展现__name__字符串的益处。

    类型对象是一个内建类型的例子,它有__name__的属性。回忆一下,type()返回被调用对象的类型。
    咱们可使用类型对象的__name__属性来取得相应的字符串名。

以下例示:
    >>> stype = type('What is your quest?')
    >>> stype                       # stype is a type object stype 是一个类型对象
    <type 'string'>
    >>> stype.__name__      # get type as a string 获得类型名(字符串表示)
    'string'

    >>> type(3.14159265)    # also a type object 又一个类型对象
    <type 'float'>
    >>> type(3.14159265).__name__   # get type as a string 获得类型名(字符串表示) 
    'float'

__doc__
    是类的文档字符串,与函数及模块的文档字符串类似,必须紧随头行(header line) 后的字符串。
    文档字符串不能被派生类继承,也就是说派生类必须含有它们本身的文档字符串。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__dict__
    包含一个字典,由类的数据属性组成。
    访问一个类属性的时候,Python 解释器将会搜索字典以获得须要的属性。
    若是在__dict__中没有找到,将会在基类的字典中进行搜索, 采用“深度优先搜索”顺序。
    基类集的搜索是按顺序的,从左到右,按其在类定义时,定义父类参数时的顺序。
    对类的修改会仅影响到此类的字典;基类的__dict__属性不会被改动的

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__module__
    Python 支持模块间的类继承。为更清晰地对类进行描述,1.5 版本中引入了__module__, 这样类名就彻底由模
块名所限定。看一下下面的例子:
    >>> class C(object): 
    ...         pass
    ...
    >>> C
    <class __main__.C at 0x53f90>

    >>> C.__module__
    '__main__'

    类 C 的全名是“__main__.C”,好比,source_module.class_name。
    若是类 C 位于一个导入的模块中,如 mymod,像下面的: 
    >>> from mymod import C 

    >>> C
    <class mymod.C at 0x53ea0> 

    >>> C.__module__
    'mymod'

    在之前的版本中,没有特殊属性__module__,很难简单定位类的位置,由于类没有使用它们的全名。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__class__
    最后,因为类型和类的统一性,当访问任何类的__class__属性时,你将发现它就是一个类型对象的实例。换句话说,一个类已经是一种类型了。由于经典类并不认同这种等价性(一个经典类是一 个类对象,一个类型是一个类型对象),对这些对象来讲,这个属性并未定义。
__class__究竟是什么意思?看例子
    # cat a.py
    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    class A:
        def __init__(self,url):
            self.url = url
        def out(self):
            return self.url

    a = A('news.163.com')
    print a.out()

    b = a.__class__('www.bccn.net')
    print b.out()

    print A
    print a.__class__

    # python a.py
    news.163.com
    www.bccn.net
    __main__.A
    __main__.A

    能够看出a.__class__就等效于a的类A

内置类属性调用实例:
#!/usr/bin/python
class Employee:
'全部员工的基类'
empCount = 0

def __init__(self, name, salary):
      self.name = name
      self.salary = salary
      Employee.empCount += 1

   def displayCount(self):
     print "Total Employee %d" % Employee.empCount

   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary

print "Employee.__doc__:", Employee.__doc__
print "Employee.__name__:", Employee.__name__
print "Employee.__module__:", Employee.__module__
print "Employee.__bases__:", Employee.__bases__
print "Employee.__dict__:", Employee.__dict__

输出结果:
Employee.__doc__: 全部员工的基类
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount': <function displayCount at 0x10a939c80>, 'empCount': 0, 'displayEmployee': <function displayEmployee at 0x10a93caa0>, '__doc__': '\xe6\x89\x80\xe6\x9c\x89\xe5\x91\x98\xe5\xb7\xa5\xe7\x9a\x84\xe5\x9f\xba\xe7\xb1\xbb', '__init__': <function __init__ at 0x10a939578>}

类的继承

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是经过继承机制。
继承彻底能够理解成类之间的类型和子类型关系。

继承语法

class 派生类名(基类名):

python继承中的特色:
1:在继承中基类的构造(init()方法)不会被自动调用,它须要在其派生类的构造中亲自专门调用。
2:在调用基类的方法时,须要加上基类的类名前缀,且须要带上self参数变量。区别于在类中调用普通函数时并不须要带上self参数
3:Python老是首先查找对应类型的方法,若是它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。

多重继承

若是在继承元组中列了一个以上的类,那么它就被称做"多重继承" 。
语法:
派生类的声明,与他们的父类相似,继承的基类列表跟在类名以后,以下所示:

class SubClassName (ParentClass1[, ParentClass2, ...]):
   'Optional class documentation string'
   class_suite

实例:
#!/usr/bin/python
class Parent: # 定义父类
parentAttr = 100
def init(self):
print "调用父类构造函数"

def parentMethod(self):
      print '调用父类方法'

   def setAttr(self, attr):
      Parent.parentAttr = attr

   def getAttr(self):
      print "父类属性 :", Parent.parentAttr

class Child(Parent): # 定义子类
   def __init__(self):
      print "调用子类构造方法"

   def childMethod(self):
      print '调用子类方法 child method'

c = Child()              # 实例化子类
c.childMethod()      # 调用子类的方法
c.parentMethod()   # 调用父类方法
c.setAttr(200)        # 再次调用父类的方法
c.getAttr()              # 再次调用父类的方法

以上代码执行结果以下:
调用子类构造方法
调用子类方法 child method
调用父类方法
父类属性 : 200

继承多个类

class A: # 定义类 A
.....

class B: # 定义类 B
.....

class C(A, B): # 继承类 A 和 B
.....

使用issubclass()或者isinstance()方法来检测。
issubclass()布尔函数判断一个类是另外一个类的子类或者子孙类,语法:
issubclass(sub,sup)

例子:

def foo():  
        pass                
    print issubclass(foo.__class__, object) 

    结果:        
    True
上述代码说明了Python 中的函数是 object 的子类

isinstance(obj, Class) 布尔函数若是obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。

方法重写

若是你的父类方法的功能不能知足你的需求,你能够在子类重写你父类的方法:

实例:

#!/usr/bin/python
class Parent:        # 定义父类
   def myMethod(self):
      print '调用父类方法'

class Child(Parent): # 定义子类
   def myMethod(self):
      print '调用子类方法'

c = Child()               # 子类实例
c.myMethod()         # 子类调用重写方法

输出结果:
    调用子类方法

基础重载方法

下表列出了一些通用的功能,你能够在本身的类重写:

| 序号 | 方法 描述 简单的调用 |
| 1 | __init__ ( self [,args...] )
构造函数
简单的调用方法: obj = className(args) |
| 2 | __del__( self )
析构方法, 删除一个对象
简单的调用方法 : del obj |
| 3 | __repr__( self )
转化为供解释器读取的形式
简单的调用方法 : repr(obj) |
| 4 | __str__( self )
用于将值转化为适于人阅读的形式
简单的调用方法 : str(obj) |
| 5 | __cmp__ ( self, x )
对象比较
简单的调用方法 : cmp(obj, x) |

运算符重载

Python一样支持运算符重载,实例以下:
#!/usr/bin/python
class Vector:
def init(self, a, b):
self.a = a
self.b = b

def str(self):
return 'Vector (%d, %d)' % (self.a, self.b)

def add(self,other):
return Vector(self.a + other.a, self.b + other.b)

v1 = Vector(2,10)
v2 = Vector(5,-2)
print v1 + v2
以上代码执行结果以下所示:
Vector(7,8)

类、实例和其余对象的内建函数

issubclass()
issubclass() 布尔函数判断一个类是另外一个类的子类或子孙类。

语法:
    issubclass(sub, sup)

issubclass() 返回True的状况:
   1.  给出的子类sub确实是父类sup的一个子类(反之,则为False)。 
   2.  这个函数也容许“不严格”的子类,意味着,一个类可视为其自身的子类,因此,这个函数若是当 sub 就是 sup,或者从 sup 派生而来,则返回 True。(一个“严格的”子类是严格意义上的从一个类派生而来的子类。)
   3.  从 Python 2.3 开始,issubclass()的第二个参数能够是可能的父类组成的 tuple(元组),这时, 只要第一个参数是给定元组中任何一个候选类的子类时,就会返回 True。

isinstance()
isinstance() 布尔函数在断定一个对象是不是另外一个给定类的实例时,很是有用。

语法:
    isinstance(obj1, obj2)
    isinstance()在 obj1 是类 obj2 的一个实例,或者是 obj2 的子类的一个实例时,返回 True (反之,则为 False)

例子:
    >>> class C1(object): pass
    >>> class C2(object): pass
    >>> c1 = C1()
    >>> c2 = C2()
    >>> isinstance(c1, C1) True
    >>> isinstance(c2, C1) False
    >>> isinstance(c1, C2) False
    >>> isinstance(c2, C2) True
    >>> isinstance(C2, c2) 
    Traceback (innermost last):
    File "<stdin>", line 1, in ?
    isinstance(C2, c2)
    TypeError: second argument must be a class

注意:第二个参数应当是类;否则,你会获得一个 TypeError。但若是第二个参数是一个类型对象,则不会出现异常。这是容许的,由于你也可使用 isinstance()来检查一个对象 obj1 是不是 obj2 的类型,好比:
    >>> isinstance(4, int)
    True
    >>> isinstance(4, str)
    False
    >>> isinstance('4', str)
    True

isinstance()也可使用一个元组(tuple)做为第二个参数。若是第一个参数是第二个参数中给定元组的任何一个候选类型或类的实例时,就会返回 True。

hasattr(), getattr(),setattr(), delattr()
*attr()系列函数能够在各类对象下工做,不限于类(class)和实例(instances),由于在类和实例中使用极其频繁,就在这里列出来了。

hasattr()函数是 Boolean 型的,它的目的就是为了决定一个对象是否有一个特定的属性,通常用于访问某属性前先做一下检查。

getattr()取得对象的属性,会在你试图读取一个不存在的属性时,引起 AttributeError 异常,除非给出那个可选的默认参数。

setattr()将要么加入一个新的属性,要么取代一个已存在的属性。

delattr()函数会从一个对象中删除属性。

下面一些例子使用到了*attr()系列函数:

>>> class myClass(object): 
...         def __init__(self): 
...             self.foo = 100 
>>> myInst = myClass()
>>> hasattr(myInst, 'foo')
True
>>> getattr(myInst, 'foo')
100
>>> hasattr(myInst, 'bar') 
False
>>> getattr(myInst, 'bar') 
Traceback (most recent call last):
File "<stdin>", line 1, in ?
getattr(myInst, 'bar')
AttributeError: myClass instance has no attribute 'bar'
>>> getattr(c, 'bar', 'oops!')
'oops!'
>>> setattr(myInst, 'bar', 'my attr')
>>> dir(myInst)
['__doc__', '__module__', 'bar', 'foo']
>>> getattr(myInst, 'bar')       # same as myInst.bar 
'my attr'
>>> delattr(myInst, 'foo')
>>> dir(myInst)
['__doc__', '__module__', 'bar']
>>> hasattr(myInst, 'foo')
False

dir()
用 dir()列出一个模块全部属性的信息, dir()还能够用在对象上。
dir()提供的信息比之前更加详尽。根据文档,“除了实例变量名和经常使用方法外,它还显示那些经过特殊标记来调用的方法,像iadd(+=),len(len()), ne(!=)。
dir()做用在实例上(经典类或新式类)时,显示实例变量,还有在实例所在的类及全部它的基类中定义的方法和类属性.
dir()做用在类上(经典类或新式类)时,则显示类以及它的全部基类的dict中的内容。 但它不会显示定义在元类(metaclass)中的类属性.
dir()做用在模块上时,则显示模块的dict的内容.
dir()不带参数时,则显示调用者的局部变量.

super()
super()函数的目的就是帮助程序员找出相应的父类, 而后方便调用相关的属性。通常状况下,程序员可能仅仅采用非绑定方式调用祖先类方法。使用 super()能够简化搜索一个合适祖先的任务,而且在调用它时,替你传入实例或类型对象。
每一个定义的类,都有一个名为mro的属性,它是一个元组,按照他们被搜索时的顺序,列出了备搜索的类。
语法以下:
super(type[, obj])
给出 type,super()“返回此 type 的父类”。若是你但愿父类被绑定,你能够传入 obj 参数(obj 必须是
type 类型的).不然父类不会被绑定。obj 参数也能够是一个类型,但它应当是 type 的一个子类。一般,当给出 obj 时:

  1. 若是obj是一个实例,isinstance(obj,type)就必须返回True
  2. 若是obj是一个类或类型,issubclass(obj,type)就必须返回True

事实上,super()是一个工厂函数,它创造了一个 super object,为一个给定的类使用mro 去查找相应的父类。很明显,它从当前所找到的类开始搜索 MRO。
super()的主要用途,是来查找父类的属性,好比, super(MyClass,self).init()。若是你没有执行这样的查找,你可能不须要使用 super()。

vars()
vars()内建函数与 dir()类似,只是给定的对象参数都必须有一个dict属性。vars()返回一个字典,它包含了对象存储于其dict中的属性(键)及值。若是提供的对象没有这样一个属性, 则会引起一个 TypeError 异常。若是没有提供对象做为 vars()的一个参数,它将显示一个包含本地名字空间的属性(键)及其值的字典,也就是locals()。

例子,使用类实例调用 vars():

>>> class C(object):
    ...         pass
    >>> c = C()
    >>> c.foo = 100
    >>> c.bar = 'Python'
    >>> c.__dict__
    {'foo': 100, 'bar': 'Python'}
    >>> vars(c)
     {'foo': 100, 'bar': 'Python'}

Python新式类和经典类

Python从2.2开始,引入了 new style class(新式类)
Python 2.x中默认都是经典类,只有显式继承了object才是新式类
Python 3.x中默认都是新式类,没必要显式的继承object

新式类跟经典类的差异主要是如下几点:
• 新式类对象能够直接经过class属性获取自身类型:type
• 继承搜索的顺序发生了改变
• 新式类增长了slots内置属性, 能够把实例属性的种类锁定到slots规定的范围之中
• 新式类增长了getattribute方法

1. 新式类对象能够直接经过class属性获取自身类型:type

#cat test1.py
#/usr/bin/env python2.7
    class E:    
    #经典类  
        pass  

    class E1(object):    
    #新式类  
        pass  

    e = E()  
    print "经典类"  
    print e  
    print type(e)  
    print e.__class__  

    print "新式类"  
    e1 = E1()  
    print e1  
    print e1.__class__  
    print type(e1)

输出结果:
    经典类  
    <__main__.E instance at 0x0000000002250B08>  
    <type 'instance'>  
    __main__.E  

    新式类  
    <__main__.E1 object at 0x0000000002248710>  
    <class '__main__.E1'>  
    <class '__main__.E1'> 

    E1是定义的新式类。那么输出e1的时候,不管是type(e1),仍是e1.__class__都是输出的<class '__main__.E1'>。

2. 继承搜索的顺序发生了改变

经典类多继承属性搜索顺序: 先深刻继承树左侧,再返回,开始找右侧
新式类多继承属性搜索顺序: 先水平搜索,而后再向上移动

#cat test2.py
    class A(object):    
        '新式类 ,做为全部类的基类'
        def foo(self):    
            print "class A"   

    class A1():    
        '经典类,做为全部类的基类 '
        def foo(self):    
            print "class A1"    

    class C(A):    
        pass  

    class C1(A1):    
        pass  

    class D(A):    
        def foo(self):    
            print "class D"    

    class D1(A1):    
        def foo(self):    
            print "class D1"    

    class E(C, D):    
        pass  

    class E1(C1, D1):    
        pass  

    e = E()  
    e.foo()  

    e1 = E1()  
    e1.foo() 

输出结果:
    class D  
    class A1  

由于A新式类,对于继承A类都是新式类,首先要查找类E中是否有foo(),若是没有则按顺序查找C->D->A。它是一种广度优先查找方式。

由于A1经典类,对于继承A1类都是经典类,首先要查找类E1中是否有foo(),若是没有则按顺序查找C1->A1->D1。它是一种深度优先查找方式。

3. 新式类增长了slots内置属性, 能够把实例属性的种类锁定到slots规定的范围之中。

好比只容许对A实例添加name和age属性:

#cat test3.py
    class A(object):    
        __slots__ = ('name', 'age')   

    class A1():    
        __slots__ = ('name', 'age')   

    a1 = A1()  
    a = A()  

    a1.name1 = "a1"  
    a.name1 = "a" 

A是新式类添加了__slots__ 属性,因此只容许添加 name age
A1经典类__slots__ 属性没用

执行结果:
     Traceback (most recent call last):  
       File "t.py", line 13, in <module>  
         a.name1 = "a"  
     AttributeError: 'A' object has no attribute 'name1'  
因此a.name是会出错的

一般每个实例都会有一个__dict__属性,用来记录实例中全部的属性和方法,也是经过这个字典,可让实例绑定任意的属性。
__slots__属性做用就是,当类C有比较少的变量,并且拥有__slots__属性时,类C的实例就没有__dict__属性,而是把变量的值存在一个固定的地方。若是试图访问一个__slots__中没有的属性,实例就会报错。
这样操做的好处:__slots__属性虽然令实例失去了绑定任意属性的便利,可是由于每个实例没有__dict__属性,却能有效节省每个实例的内存消耗,有利于生成小而精干的实例。

4. 新式类增长了getattribute方法

#cat test4.py
class A(object):    
    def __getattribute__(self, *args, **kwargs):    
        print "A.__getattribute__"  

class A1():    
    def __getattribute__(self, *args, **kwargs):    
        print "A1.__getattribute__"  

a1 = A1()  
a = A()  

a.test  
print "========="  
a1.test

A.__getattribute__  
=========  
Traceback (most recent call last):  
    File "t.py", line 18, in <module>  
    a1.test  
AttributeError: A1 instance has no attribute 'test'  

A是新式类,每次经过实例访问属性,都会通过__getattribute__函数,
A1不会调用__getattribute__因此出错了
相关文章
相关标签/搜索