1. 程式人生 > >python學習筆記05-python面向物件程式設計

python學習筆記05-python面向物件程式設計

1.面向物件程式設計

1. 類和例項

注意,類的所有方法,的第一個引數一定是self。
在呼叫類的方法時,不需要傳入第一個引數self。 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#定義一個類Student,繼承自object
class Student(object):
        #構造器
        def __init__(self,name,score):
                #將name和score儲存的私有成員變數中
                self.__name = name
                self.__score = score
        #定義一個公有的show方法
        def show(self):
                print 'name =%s,score =%.2f' %(self.__name,self.__score)
s = Student(18,99.5)
s.show()
#用這種方式去訪問類中私有的變數,是非常不建議的。因為,在不同的python版本中,可能會不同。
#正確的做法應該是新增setter和getter方法。
#print s._Student__name

2.繼承和多型

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Person(object):
        def show(self):
                print 'Person show'
#繼承Person類
class Student(Person):
        def show(self):
                print 'Student show'
#多型
def run(p):
        p.show()
run(Person())
run(Student())

3. 獲取物件資訊

判斷物件型別,使用type()函式
Python把每種type型別都定義好了常量,放在types模組裡,使用之前,需要先匯入:types模組
所有型別本身的型別就是TypeType 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Person(object):
        def show(self):
                print 'Person show'
class Student(Person):
        def show(self):
                print 'Student show'
p = Person()
s = Student()
print type(123)
print type('abc')
print type(int)
print type(p)
print type(s)
import types
print type('abc') == types.StringType

執行結果 

<type 'int'>
<type 'str'>
<type 'type'>
<class '__main__.Person'>
<class '__main__.Student'>
True

4.isinstance詳解

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Person(object):
        def show(self):
                print 'Person show'
class Student(Person):
        def show(self):
                print 'Student show'
p = Person()
s = Student()
#判斷是否是某個類的例項
print isinstance(p,Person)
print isinstance(s,Person)
print isinstance(p,Student)
#判斷是否是某兩個類中任意一個類的例項
print isinstance(p,(Student,Person))
#判斷是str或者unicode型別中的任意一個
print isinstance('a',(str,unicode))
print isinstance(u'a',(str,unicode))
#由於str和unicode都是從basestring繼承下來的,所以,還可以把上面的程式碼簡化為:
print isinstance(u'a',basestring)

5.使用dir()

如果要獲得一個物件的所有屬性和方法,可以使用dir()函式,它返回一個包含字串的list
類似xxx的屬性和方法在Python中都是有特殊用途的,比如len方法返回長度。在Python中,如果你呼叫len()函式試圖獲取一個物件的長度,實際上,在len()函式內部,它自動去呼叫該物件的len()方法
getattr()、setattr()以及hasattr()
如果試圖獲取不存在的屬性,會丟擲AttributeError的錯誤。可以傳入一個default引數,如果屬性不存在,就返回預設值 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Person(object):
        def show(self):
                print 'Person show'
p = Person()
print dir(Person) == dir(p)
#獲取物件的所有屬性和方法
dir(p)
#呼叫len('ABC')等同於呼叫'ABC'.__len__()
print len('ABC')
print 'ABC'.__len__()
#判斷物件是否包含某個屬性,如果不包含,則新增則屬性
if hasattr(p,'score'):
        print p.score
else:
        setattr(p,'score',65.5)
        print p.score
def err():
        print 'no method'
#獲取一個物件的某個屬性,並傳入預設值。
fn1 = getattr(p,'show',err)
fn2 = getattr(p,'show2',err)
fn1()
fn2()

2.面向物件高階程式設計

1.為物件或者類動態繫結屬性或者方法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Person(object):
        def show(self):
                print 'Person show'
p = Person()
#為物件動態繫結屬性
p.name = 'zhangsan'
print p.name
#匯入MethodType模組,以便為物件或類動態繫結方法
from types import MethodType
def test(self):
        print 'test succ'
        self.show()
#為物件動態繫結方法(此方法只會作用在當前物件上)
p.test = MethodType(test,p,Person)
p.test()
#為類動態繫結方法(此方法會作用在所有物件上)
Person.test = MethodType(test,None,Person)
p2 = Person()
p2.test(

2.使用slots限定class可以繫結的屬性

Python允許在定義class的時候,定義一個特殊的slots變數,來限制該class能新增的屬性
注意,子類不受父類的slots的限制
如果子類也定義了slots,那麼他可以新增的屬性為父類中slots定義的和子類自己的slots

class Student(object):
     __slots__ = ('name', 'age') # 用tuple定義允許繫結的屬性名稱
    ...省略...

[email protected]

作用:把一個方法變為屬性。(其實是簡化getter和setter的呼叫,讓getter和setter方法可以像直接操作屬性一樣簡單)
把一個getter方法變成屬性,只需要加上@property就可以了,此時,@property本身又建立了另一個裝飾器@score.setter,負責把一個setter方法變成屬性賦值 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Student(object):
        @property
        def birth(self):
                return self.__birth
        @birth.setter
        def birth(self,value):
                self.__birth = value
        @property
        def age(self):
                return 2016 - self.__birth
s = Student()
s.birth = 1991
print 'birth =',s.birth
print 'age =',s.age

4.多重繼承

在設計類的繼承關係時,通常,主線都是單一繼承下來的,例如,Ostrich繼承自Bird。但是,如果需要“混入”額外的功能,通過多重繼承就可以實現,比如,讓Ostrich除了繼承自Bird外,再同時繼承Runnable。這種設計通常稱之為Mixin。 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Animal(object):
        pass
class Mammal(Animal):
        pass
class Bird(Animal):
        pass
class RunnableMixin(object):
        def run(self):
                print('Running...')
class FlyableMixin(object):
        def fly(self):
                print('Flying...')
#多重繼承(同時繼承自Mammal和RunnableMixin)
class Dog(Mammal,RunnableMixin):
        pass
#多重繼承(同時繼承自Mammal和FlyableMixin)
class Bat(Mammal,FlyableMixin):
        pass
d = Dog()
b = Bat()
d.run()
b.fly()

5.定製類

1.str和repr-定製顯示資訊

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Student(object):
        #構造器
        def __init__(self,name):
                self.__name = name
        #定製給使用者看的類資訊
        #當呼叫print xxx時,會呼叫__str__方法
        def __str__(self):
                return 'Student object (name:%s)' % self.__name
        #定製給程式設計師看的類資訊
        #當在互動終端直接輸入xxx時,會呼叫__repr__方法
        __repr__ = __str__
s = Student('zhangsan')
print s

2.iter、next、getitem-讓一個物件成為可迭代物件

讓一個物件成為一個可以被迭代,可以使用下標索引元素,可以切片的物件。 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Fib(object):
        def __init__(self):
                self.a,self.b = 0,1 #初始化兩個計數器a,b
        #對迭代的支援
        def __iter__(self):
                return self #例項本身就是迭代物件,故返回自己
        #對迭代的支援
        def next(self):
                self.a,self.b = self.b,self.a + self.b
                if self.a > 10000:
                        raise StopIteration()
                return self.a #返回下一個值
        def __getitem__(self,n):
                #對使用下標索引元素的支援
                if isinstance(n,int):
                        a,b = 1,1
                        for x in range(n):
                                a,b = b,a+b
                        return a
                #對切片方法的支援
                if isinstance(n,slice):
                        start = n.start
                        stop = n.stop
                        a,b = 1,1
                        L = []
                        for x in range(stop):
                                if x >= start:
                                        L.append(a)
                                a,b = b,a+b
                        return L
f = Fib()
for i in f:
        print i
print '\n'
print f[2]
print '\n'
print f[2:4]

3.getattr-訪問不存在的屬性或方法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Student(object):
        #當且僅當物件的屬性或方法不存在的時候,才會呼叫getattr方法
        def __getattr__(self,attr_name):
                if attr_name == 'age':
                        return lambda:25
s = Student()
print s.age()

4.call-把例項變數當函式呼叫

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Student(object):
        def __init__(self,age):
                self.__age = age
        def __call__(self,age):
                return age
        def getAge(self):
                return self.__age
s = Student(18)
#此時呼叫的為getAge方法
print s.getAge()
#判斷例項變數是否可以直接當函式呼叫
if(callable(s)):
        #直接把物件當方法呼叫,則呼叫的為__call__方法
        print s(29)
else:
        print 'can not call instance'

6.使用type()函式建立一個新的型別

要建立一個class物件,type()函式依次傳入3個引數: 

  1. class的名稱
  2. 繼承的父類集合,注意Python支援多重繼承,如果只有一個父類,別忘了tuple的單元素寫法;
  3. class的方法名稱與函式繫結
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def fn(self,name='world'): #先定義函式
        print('Hello,%s' % name)
Hello = type('Hello',(object,),dict(hello=fn)) #建立Hello class
h = Hello()
h.hello()
print(type(Hello))
print(type(h))

7.使用metaclass建立一個list類的子類

new方法接收到的引數依次是:
1. 當前準備建立的類的物件
2. 類的名字
3. 類繼承的父類集合
4. 類的方法集合 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# metaclass是建立類,所以必須從'type'型別派生
class ListMetaclass(type):
        def __new__(cls,name,bases,attrs):
                attrs['add'] = lambda self,value: self.append(value)
                return type.__new__(cls,name,bases,attrs)
class MyList(list):
        __metaclass__ = ListMetaclass  #指示使用ListMetaclass來定製類
L = MyList()
L.add(1)
L.add('aa')
print L

8.ORM——metaclass使用場景

當用戶定義一個class User(Model)時,Python直譯器首先在當前類User的定義中查詢metaclass,如果沒有找到,就繼續在父類Model中查詢metaclass,找到了,就使用Model中定義的metaclass的ModelMetaclass來建立User類,也就是說,metaclass可以隱式的繼承到子類,但子類自己卻感覺不到。
在ModelMetaclass中,一共做了幾件事情:
1. 排除掉對Model類的修改
2. 在當前類(比如User)中查詢定義的類的所有屬性,如果找到一個Field屬性,就把它儲存到一個mappings的dict中,同時從類屬性中刪除該Field屬性,否則,容易執行時錯誤
3. 把表名儲存到table中,這裡簡化為表名預設為類名 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Field(object):
        def __init__(self,name,column_type):
                self.name = name
                self.column_type = column_type
        def __str__(self):
                return '<%s:%s>' % (self.__class__.__name__,self.name)
class StringField(Field):
        def __init__(self,name):
                super(StringField,self).__init__(name,'varchar(100)')
class IntegerField(Field):
        def __init__(self,name):
                super(IntegerField,self).__init__(name,'bigint')
class ModelMetaclass(type):
        def __new__(cls,name,bases,attrs):
                #當Model類建立時,會進入if條件中,然後使用type建立類後,直接返回Model類
                if name=='Model':
                        print '--->attrs:%s' % attrs
                        return type.__new__(cls,name,bases,attrs)
                print '<--->attrs:%s' % attrs
                #當User類建立時,不會進入if條件,而會執行下面的邏輯
                mappings = dict()
                #對User類中的所有屬性或者方法進行遍歷,發現有值為Field型別的屬性,則將屬性名以及此屬性的值(注意,屬性的值其實是一個Field的子類型別)加入到mappings中
                #此Mappings用於記錄User類中每個屬性都是什麼型別的
                for k,v in attrs.iteritems():
                        if isinstance(v,Field):
                                print('Found mapping:%s==>%s' % (k,v))
                                mappings[k] = v
                #將User類中所有Field型別的屬性全部刪除
                for k in mappings.iterkeys():
                        attrs.pop(k)
                #將__table__屬性的值設定為類名
                attrs['__table__'] = name #假設表名和類名一致
                #將__mappings__屬性的值設定為儲存有"列名<--->類型別"對映關係的mappings
                attrs['__mappings__'] = mappings #儲存屬性和列的對映關係
                #修改過attrs,使用type建立一個類,並返回
                return type.__new__(cls,name,bases,attrs)