歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Python基礎教程:面向對象編程

Python基礎教程:面向對象編程

日期:2017/3/1 9:15:57   编辑:Linux編程

模塊和包

: 文件夾 (可以有多級), 且包含__init__.py文件(每層都要有) 模塊: py文件

代碼分開放在多個py文件(模塊名=文件名). 同名變量互不影響.

模塊名沖突: 把同名模塊放在不同中.

導入模塊

from math import log
from logging import log as logger

引用時: 使用完整的路徑(包+模塊名). ex. p1.util.f()

動態導入模塊

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

上述代碼先嘗試從cStringIO導入,如果失敗了(比如cStringIO沒有被安裝),再嘗試從StringIO導入。這樣,如果cStringIO模塊存在,則我們將獲得更快的運行速度,如果cStringIO不存在,則頂多代碼運行速度會變慢,但不會影響代碼的正常執行。

使用__future__

Python的新版本會引入新的功能,但是,實際上這些功能在上一個老版本中就已經存在了。要“試用”某一新的特性,就可以通過導入__future__模塊的某些功能來實現。

ex. 在Python 2.7中引入3.x的除法規則,導入__future__的division:

>>> from __future__ import division
>>> print 10 / 3
3.3333333333333335

安裝第三方模塊

模塊管理工具:

  • easy_install
  • pip (推薦)

查找第三方模塊: https://pypi.python.org/pypi

面向對象編程基礎

OOP: 數據的封裝

初始化實例屬性

當創建實例時,__init__()方法被自動調用, 第一個參數必須是 self(也可以用別的名字,但建議使用習慣用法, 第一個參數self被Python解釋器作為實例的引用),後續參數則可以自由指定,和定義函數沒有任何區別。
相應地,創建實例時,就必須要提供除 self 以外的參數.

setattr__init__接受任意的kw參數:

setattr(object, name, value)
This is the counterpart of getattr(). The arguments are an object, a string and an arbitrary value. The string may name an existing attribute or a new attribute. The function assigns the value to the attribute, provided the object allows it. For example, setattr(x, 'foobar', 123) is equivalent to x.foobar = 123.

class Person(object):  
    def __init__(self, name, gender, birth, **kw):  
        self.name = name  
        self.gender = gender  
        self.birth = birth  
        for k, v in kw.iteritems():  
            setattr(self, k, v)

訪問限制

Python對屬性權限的控制是通過屬性名來實現的.

  • 如果一個屬性由雙下劃線開頭(__),該屬性就無法被外部訪問。
  • 但是,如果一個屬性以"__xxx__"的形式定義,那它又可以被外部訪問了,以"__xxx__"定義的屬性在Python的類中被稱為特殊屬性有很多預定義的特殊屬性可以使用,通常我們不要把普通屬性用"xxx"定義。
  • 以單下劃線開頭的屬性"_xxx"雖然也可以被外部訪問,但是,按照習慣,他們不應該被外部訪問。

創建類屬性

綁定在一個實例上的屬性不會影響其他實例,但是,類本身也是一個對象,如果在類上綁定一個屬性,則所有實例都可以訪問類的屬性,並且,所有實例訪問的類屬性都是同一個!也就是說,實例屬性每個實例各自擁有,互相獨立,而類屬性有且只有一份
定義類屬性可以直接在 class 中定義:

class Person(object):  
    address = 'Earth'  
    def __init__(self, name):  
        self.name = name

因為類屬性是直接綁定在類上的,所以,訪問類屬性不需要創建實例,就可以直接訪問. 對一個實例調用類的屬性也是可以訪問的,所有實例都可以訪問到它所屬的類的屬性.

print Person.address  
print p1.address

類屬性和實例屬性名字沖突怎麼辦
當實例屬性和類屬性重名時,實例屬性優先級高,它將屏蔽掉對類屬性的訪問。
可見,千萬不要在實例上修改類屬性,它實際上並沒有修改類屬性,而是給實例綁定了一個實例屬性。

定義實例方法

實例的方法就是在類中定義的函數,它的第一個參數永遠是 self,指向調用該方法的實例本身,其他參數和一個普通函數是完全一樣的. 在實例方法內部,可以訪問所有實例屬性,這樣,如果外部需要訪問私有屬性,可以通過方法調用獲得,這種數據封裝的形式除了能保護內部數據一致性外,還可以簡化外部調用的難度。

我們在 class 中定義的實例方法其實也是屬性,它實際上是一個函數對象. 因為方法也是一個屬性,所以,它也可以動態地添加到實例上,只是需要用 types.MethodType() 把一個函數變為一個方法...

定義類方法

和屬性類似,方法也分實例方法和類方法。
在class中定義的全部是實例方法,實例方法第一個參數 self 是實例本身。
要在class中定義類方法,需要這麼寫:

class Person(object):  
    count = 0  
    @classmethod  
    def how_many(cls):  
        return cls.count  
    def __init__(self, name):  
        self.name = name  
        Person.count = Person.count + 1  
print Person.how_many()  
p1 = Person('Bob')  
print Person.how_many()

通過標記一個 @classmethod,該方法將綁定到 Person 類上,而非類的實例。類方法的第一個參數將傳入類本身,通常將參數名命名為 cls,上面的 cls.count 實際上相當於 Person.count。

類的繼承

代碼復用

python的繼承:

  • 總是從某個類繼承(最上層是object)
  • 不要忘記super.__init__調用

super(SubCls, self)將返回當前類繼承的父類, 注意self參數已在super()中傳入,在__init__()中將隱式傳遞,不需要寫出(也不能寫)。
def init(self, args):
super(SubCls, self).init(args)
pass

判斷類型

函數isinstance()可以判斷一個變量的類型,既可以用在Python內置的數據類型如str、list、dict,也可以用在我們自定義的類,它們本質上都是數據類型。

>>> isinstance(p, Person)  
True    # p是Person類型  
>>> isinstance(p, Student)  
False   # p不是Student類型  
>>> isinstance(p, Teacher)  
False   # p不是Teacher類型  
>>> isinstance(s, Person)  
True    # s是Person類型

在一條繼承鏈上,一個實例可以看成它本身的類型,也可以看成它父類的類型。

多態

調用 s.whoAmI()總是先查找它自身的定義,如果沒有定義,則順著繼承鏈向上查找,直到在某個父類中找到為止。

由於Python是動態語言,所以,傳遞給函數 who_am_i(x)的參數 x 不一定是 Person 或 Person 的子類型。任何數據類型的實例都可以,只要它有一個whoAmI()的方法即可:

class Book(object):  
    def whoAmI(self):  
        return 'I am a book'

這是動態語言和靜態語言(例如Java)最大的差別之一。動態語言調用實例方法,不檢查類型,只要方法存在,參數正確,就可以調用

多重繼承

除了從一個父類繼承外,Python允許從多個父類繼承,稱為多重繼承。

class A(object):  
    def __init__(self, a):  
        print 'init A...'  
        self.a = a

class B(A):  
    def __init__(self, a):  
        super(B, self).__init__(a)  
        print 'init B...'

class C(A):  
    def __init__(self, a):  
        super(C, self).__init__(a)  
        print 'init C...'

class D(B, C):  
    def __init__(self, a):  
        super(D, self).__init__(a)  
        print 'init D...'


D 同時繼承自 B 和 C,也就是 D 擁有了 A、B、C 的全部功能。多重繼承通過 super()調用__init__()方法時,A 雖然被繼承了兩次,但__init__()只調用一次:

>>> d = D('d')  
init A...  
init C...  
init B...  
init D...

獲取對象信息

首先可以用 type() 函數獲取變量的類型,它返回一個 Type 對象:

>>> type(123)  
<type 'int'>  
>>> s = Student('Bob', 'Male', 88)  
>>> type(s)  
<class '__main__.Student'>

其次,可以用 dir() 函數獲取變量的所有屬性:

>>> dir(123)   # 整數也有很多屬性...  
['__abs__', '__add__', '__and__', '__class__', '__cmp__', ...]

>>> dir(s)  
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'gender', 'name', 'score', 'whoAmI']

dir()返回的屬性是字符串列表,如果已知一個屬性名稱,要獲取或者設置對象的屬性,就需要用 getattr()setattr()函數了:

>>> getattr(s, 'name')  # 獲取name屬性  
'Bob'  
>>> setattr(s, 'name', 'Adam')  # 設置新的name屬性  
>>> s.name  
'Adam'  
>>> getattr(s, 'age')  # 獲取age屬性,但是屬性不存在,報錯:  
Traceback (most recent call last):  
  File "<stdin>", line 1, in <module>  
AttributeError: 'Student' object has no attribute 'age'  
>>> getattr(s, 'age', 20)  # 獲取age屬性,如果屬性不存在,就返回默認值20:  
20

class Person(object):  
    def __init__(self, name, gender, **kw):  
        for k,v in kw.iteritems():  
            setattr(self, k, v)

p = Person('Bob', 'Male', age=18, course='Python')  
print p.age  
print p.course

定制類

特殊方法

又叫 "魔術方法"

  • 定義在class中
  • 不需要直接調用: py的函數或操作符會自動調用

ex. 任何數據類型的實例都有__str__()特殊方法.

pothon的特殊方法:

  • __str__: 用於print
  • __len__: 用於len
  • __cmp__: 用於比較cmp/排序sorted

strrepr

實現特殊方法__str__()可以在print的時候打印合適的字符串, 如果直接在命令行敲變量名則不會:

>>> p = Person('Bob', 'male')  
>>> print p  
(Person: Bob, male)  
>>> p  
<main.Person object at 0x10c941890>

因為 Python 定義了__str__()__repr__()兩種方法,__str__()用於顯示給用戶,而__repr__()用於顯示給開發人員。
偷懶定義__repr__: __repr__ = __str__

cmp

__cmp__用實例自身self和傳入的實例 s 進行比較,如果 self 應該排在前面,就返回 -1,如果 s 應該排在前面,就返回1,如果兩者相當,返回 0。

class Student(object):  
    def __init__(self, name, score):  
        self.name = name  
        self.score = score  
    def __str__(self):  
        return '(%s: %s)' % (self.name.lower(), self.score)  
    __repr__ = __str__  
    def __cmp__(self, s):  
        if self.score!=s.score:  
            return - (self.score - s.score)  
        else: return cmp(self.name, s.name)

len

如果一個類表現得像一個list,要獲取有多少個元素,就得用 len() 函數。
要讓 len() 函數工作正常,類必須提供一個特殊方法__len__(),它返回元素的個數。

數學運算

如果要讓Rational類(有理數)進行+運算,需要正確實現__add__

class Rational(object):  
    def __init__(self, p, q):  
        self.p = p  
        self.q = q

p、q 都是整數,表示有理數 p/q。

class Rational(object):  
    def __init__(self, p, q):  
        self.p = p  
        self.q = q  
    def __add__(self, r):  
        return Rational(self.p * r.q + self.q * r.p, self.q * r.q)  
    def __sub__(self, r):  
        return Rational(self.p * r.q - self.q * r.p, self.q * r.q)  
    def __mul__(self, r):  
        return Rational(self.p * r.p, self.q * r.q)  
    def __div__(self, r):  
        return Rational(self.p * r.q, self.q * r.p)  
    def __str__(self):  
        d = 1  
        for i in xrange(2,min(self.p, self.q)+1):  
            if self.p%i==0 and self.q%i==0:  
                d = i  
        return '%s/%s' % (self.p/d, self.q/d)  
    __repr__ = __str__

類型轉換

要讓int()函數對於Rational類正常工作,只需要實現特殊方法__int__():
同理,要讓float()函數正常工作,只需要實現特殊方法__float__()

@property

class Student(object):  
    def __init__(self, name, score):  
        self.name = name  
        self.__score = score  
    def get_score(self):  
        return self.__score  
    def set_score(self, score):  
        if score < 0 or score > 100:  
            raise ValueError('invalid score')  
        self.__score = score

使用 get/set 方法來封裝對一個屬性封裝. 但是寫 s.get_score() 和 s.set_score() 沒有直接寫 s.score 來得直接。

可以用裝飾器函數把 get/set 方法“裝飾”成屬性調用:

class Student(object):  
    def __init__(self, name, score):  
        self.name = name  
        self.__score = score  
    @property  
    def score(self):  
        return self.__score  
    @score.setter  
    def score(self, score):  
        if score < 0 or score > 100:  
            raise ValueError('invalid score')  
        self.__score = score

第一個score(self)是get方法,用@property裝飾,第二個score(self, score)是set方法,用@score.setter裝飾,@score.setter是前一個@property裝飾後的副產品。對 score 賦值實際調用的是 set方法。

slots

由於Python是動態語言,任何實例在運行期都可以動態地添加屬性。

如果要限制添加的屬性,例如,Student類只允許添加 name、gender和score 這3個屬性,就可以利用Python的一個特殊的__slots__來實現。
顧名思義,__slots__是指一個類允許的屬性列表 (所以是類屬性):

class Student(object):  
    __slots__ = ('name', 'gender', 'score')  
    def __init__(self, name, gender, score):  
        self.name = name  
        self.gender = gender  
        self.score = score

>>> s = Student('Bob', 'male', 59)  
>>> s.name = 'Tim' # OK  
>>> s.score = 99 # OK  
>>> s.grade = 'A'  
Traceback (most recent call last):  
  ...  
AttributeError: 'Student' object has no attribute 'grade'

__slots__的目的是限制當前類所能擁有的屬性,如果不需要添加任意動態的屬性,使用__slots__也能節省內存。

call

在Python中,函數其實是一個對象:

>>> f = abs  
>>> f.__name__  
'abs'  
>>> f(-123)  
123

由於 f 可以被調用,所以,f 被稱為可調用對象。
所有的函數都是可調用對象。
一個類實例也可以變成一個可調用對象,只需要實現一個特殊方法__call__()

把 Person 類變成一個可調用對象:

class Person(object):  
    def __init__(self, name, gender):  
        self.name = name  
        self.gender = gender  
    def __call__(self, friend):  
        print 'My name is %s...' % self.name  
        print 'My friend is %s...' % friend

現在可以對 Person 實例直接調用:

>>> p = Person('Bob', 'male')  
>>> p('Tim')  
My name is Bob...  
My friend is Tim...

單看 p('Tim') 你無法確定 p 是一個函數還是一個類實例,所以,在Python中,函數也是對象,對象和函數的區別並不顯著

無需操作系統直接運行 Python 代碼 http://www.linuxidc.com/Linux/2015-05/117357.htm

CentOS上源碼安裝Python3.4 http://www.linuxidc.com/Linux/2015-01/111870.htm

《Python核心編程 第二版》.(Wesley J. Chun ).[高清PDF中文版] http://www.linuxidc.com/Linux/2013-06/85425.htm

《Python開發技術詳解》.( 周偉,宗傑).[高清PDF掃描版+隨書視頻+代碼] http://www.linuxidc.com/Linux/2013-11/92693.htm

Python腳本獲取Linux系統信息 http://www.linuxidc.com/Linux/2013-08/88531.htm

在Ubuntu下用Python搭建桌面算法交易研究環境 http://www.linuxidc.com/Linux/2013-11/92534.htm

Python 語言的發展簡史 http://www.linuxidc.com/Linux/2014-09/107206.htm

Python 的詳細介紹:請點這裡
Python 的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved