鍍金池/ 教程/ Python/ 類(3)
標(biāo)準(zhǔn)庫(kù) (4)
如何成為 Python 高手
標(biāo)準(zhǔn)庫(kù) (6)
標(biāo)準(zhǔn)庫(kù) (3)
類(2)
Pandas 使用 (2)
xml
用 tornado 做網(wǎng)站 (5)
文件(1)
練習(xí)
列表(3)
從小工到專家
除法
錯(cuò)誤和異常 (2)
函數(shù)(1)
用 tornado 做網(wǎng)站 (7)
為做網(wǎng)站而準(zhǔn)備
函數(shù)練習(xí)
標(biāo)準(zhǔn)庫(kù) (8)
Pandas 使用 (1)
回顧 list 和 str
字典(1)
用 tornado 做網(wǎng)站 (3)
字符串(1)
函數(shù)(2)
寫一個(gè)簡(jiǎn)單的程序
將數(shù)據(jù)存入文件
語(yǔ)句(5)
SQLite 數(shù)據(jù)庫(kù)
集成開發(fā)環(huán)境(IDE)
集合(1)
類(1)
用 tornado 做網(wǎng)站 (6)
用 tornado 做網(wǎng)站 (2)
自省
語(yǔ)句(4)
錯(cuò)誤和異常 (1)
用 tornado 做網(wǎng)站 (4)
集合(2)
列表(1)
標(biāo)準(zhǔn)庫(kù) (1)
生成器
mysql 數(shù)據(jù)庫(kù) (1)
第三方庫(kù)
實(shí)戰(zhàn)
運(yùn)算符
類(3)
字典(2)
語(yǔ)句(1)
數(shù)和四則運(yùn)算
語(yǔ)句(2)
文件(2)
MySQL 數(shù)據(jù)庫(kù) (2)
電子表格
迭代器
mongodb 數(shù)據(jù)庫(kù) (1)
特殊方法 (2)
特殊方法 (1)
字符編碼
編寫模塊
用 tornado 做網(wǎng)站 (1)
標(biāo)準(zhǔn)庫(kù) (5)
函數(shù)(4)
類(5)
字符串(2)
關(guān)于 Python 的故事
函數(shù)(3)
字符串(4)
處理股票數(shù)據(jù)
常用數(shù)學(xué)函數(shù)和運(yùn)算優(yōu)先級(jí)
字符串(3)
為計(jì)算做準(zhǔn)備
多態(tài)和封裝
類(4)
迭代
語(yǔ)句(3)
錯(cuò)誤和異常 (3)
分析 Hello
Python 安裝
標(biāo)準(zhǔn)庫(kù) (2)
列表(2)
元組

類(3)

在上一節(jié)中,對(duì)類有了基本的或者說(shuō)是模糊的認(rèn)識(shí),為了能夠?qū)︻愑懈羁痰恼J(rèn)識(shí),本節(jié)要深入到一些細(xì)節(jié)。

類屬性和實(shí)例屬性

正如上節(jié)的案例中,一個(gè)類實(shí)例化后,實(shí)例是一個(gè)對(duì)象,有屬性。同樣,類也是一個(gè)對(duì)象,它也有屬性。

>>> class A(object):
...     x = 7
... 

在交互模式下,定義一個(gè)很簡(jiǎn)單的類(注意觀察,有(object),是新式類),類中有一個(gè)變量 x = 7,當(dāng)然,如果愿意還可以寫別的。因?yàn)橐幌虏僮髦校挥玫竭@個(gè),我就不寫別的了。

>>> A.x
7

在類 A 中,變量 x 所引用的數(shù)據(jù),能夠直接通過(guò)類來(lái)調(diào)用。或者說(shuō) x 是類 A 的屬性,這種屬性有一個(gè)名稱,曰“類屬性”。類屬性僅限于此——類中的變量。它也有其他的名字,如靜態(tài)數(shù)據(jù)。

>>> foo = A()
>>> foo.x
7

實(shí)例化,通過(guò)實(shí)例也可以得到這個(gè)屬性,這個(gè)屬性叫做“實(shí)例屬性”。對(duì)于同一屬性,可以用類來(lái)訪問(wèn)(類屬性),在一般情況下,也可以通過(guò)實(shí)例來(lái)訪問(wèn)同樣的屬性。但是:

>>> foo.x += 1
>>> foo.x
8
>>> A.x
7

實(shí)例屬性更新了,類屬性沒有改變。這至少說(shuō)明,類屬性不會(huì)被實(shí)例屬性左右,也可以進(jìn)一步說(shuō)“類屬性與實(shí)例屬性無(wú)關(guān)”。那么,foo.x += 1 的本質(zhì)是什么呢?其本質(zhì)是該實(shí)例 foo 又建立了一個(gè)新的屬性,但是這個(gè)屬性(新的 foo.x)居然與原來(lái)的屬性(舊的 foo.x)重名,所以,原來(lái)的 foo.x 就被“遮蓋了”,只能訪問(wèn)到新的 foo.x,它的值是 8.

>>> foo.x
8
>>> del foo.x
>>> foo.x
7

既然新的 foo.x“遮蓋”了舊的 foo.x,如果刪除它,舊的不久顯現(xiàn)出來(lái)了?的確是。刪除之后,foo.x 就還是原來(lái)的值。此外,還可以通過(guò)建立一個(gè)不與它重名的實(shí)例屬性:

>>> foo.y = foo.x + 1
>>> foo.y
8
>>> foo.x
7

foo.y 就是新建的一個(gè)實(shí)例屬性,它沒有影響原來(lái)的實(shí)例屬性 foo.x。

但是,類屬性能夠影響實(shí)例屬性,這點(diǎn)應(yīng)該好理解,因?yàn)閷?shí)例就是通過(guò)實(shí)例化調(diào)用類的。

>>> A.x += 1
>>> A.x
8
>>> foo.x
8

這時(shí)候?qū)嵗龑傩愿悓傩远淖儭?/p>

以上所言,是指當(dāng)類中變量引用的是不可變數(shù)據(jù)。如果類中變量引用可變數(shù)據(jù),情形會(huì)有所不同。因?yàn)榭勺償?shù)據(jù)能夠進(jìn)行原地修改。

>>> class B(object):
...     y = [1,2,3]
...

這次定義的類中,變量引用的是一個(gè)可變對(duì)象。

>>> B.y         #類屬性
[1, 2, 3]
>>> bar = B()
>>> bar.y       #實(shí)例屬性
[1, 2, 3]

>>> bar.y.append(4)
>>> bar.y
[1, 2, 3, 4]
>>> B.y
[1, 2, 3, 4]

>>> B.y.append("aa")
>>> B.y
[1, 2, 3, 4, 'aa']
>>> bar.y
[1, 2, 3, 4, 'aa']

從上面的比較操作中可以看出,當(dāng)類中變量引用的是可變對(duì)象是,類屬性和實(shí)例屬性都能直接修改這個(gè)對(duì)象,從而影響另一方的值。

對(duì)于類屬性和實(shí)例屬性,除了上述不同之外,在下面的操作中,也會(huì)有差異。

>>> foo = A()
>>> dir(foo)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x']

實(shí)例化類 A,可以查看其所具有的屬性(看最后一項(xiàng),x),當(dāng)然,執(zhí)行 dir(A) 也是一樣的。

>>> A.y = "hello"
>>> foo.y
'hello'

增加一個(gè)類屬性,同時(shí)在實(shí)例屬性中也增加了一樣的名稱和數(shù)據(jù)的屬性。如果增加通過(guò)實(shí)例增加屬性呢?看下面:

>>> foo.z = "python"
>>> foo.z
'python'
>>> A.z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'A' has no attribute 'z'

類并沒有收納這個(gè)屬性。這進(jìn)一步說(shuō)明,類屬性不受實(shí)例屬性左右。另外,在類確定或者實(shí)例化之后,也可以增加和修改屬性,其方法就是通過(guò)類或者實(shí)例的點(diǎn)號(hào)操作來(lái)實(shí)現(xiàn),即 object.attribute,可以實(shí)現(xiàn)對(duì)屬性的修改和增加。

數(shù)據(jù)流轉(zhuǎn)

在類的應(yīng)用中,最廣泛的是將類實(shí)例化,通過(guò)實(shí)例來(lái)執(zhí)行各種方法。所以,對(duì)此過(guò)程中的數(shù)據(jù)流轉(zhuǎn)一定要弄明白。

回顧上節(jié)已經(jīng)建立的那個(gè)類,做適當(dāng)修改,讀者是否能夠?qū)懮媳匾淖⑨屇兀咳绻惆炎⑨寣懮?,就已?jīng)理解了類的基本結(jié)構(gòu)。

#!/usr/bin/env Python
# coding=utf-8

__metaclass__ = type

class Person:
    def __init__(self, name):
        self.name = name

    def getName(self):
        return self.name

    def breast(self, n):
        self.breast = n

    def color(self, color):
        print "%s is %s" % (self.name, color)

    def how(self):
        print "%s breast is %s" % (self.name, self.breast)

girl = Person('wangguniang')
girl.breast(90)

girl.color("white")
girl.how()

運(yùn)行后結(jié)果:

$ python 20701.py 
wangguniang is white
wangguniang breast is 90

一圖勝千言,有圖有真相。通過(guò)圖示,我們看一看數(shù)據(jù)的流轉(zhuǎn)過(guò)程。

http://wiki.jikexueyuan.com/project/start-learning-python/images/20801.png" alt="" />

創(chuàng)建實(shí)例 girl = Person('wangguniang'),注意觀察圖上的箭頭方向。girl 這個(gè)實(shí)例和 Person 類中的 self 對(duì)應(yīng),這正是應(yīng)了上節(jié)所概括的“實(shí)例變量與 self 對(duì)應(yīng),實(shí)例變量主外,self 主內(nèi)”的概括。"wangguniang"是一個(gè)具體的數(shù)據(jù),通過(guò)初始化函數(shù)中的 name 參數(shù),傳給 self.name,前面已經(jīng)講過(guò),self 也是一個(gè)實(shí)例,可以為它設(shè)置屬性,self.name 就是一個(gè)屬性,經(jīng)過(guò)初始化函數(shù),這個(gè)屬性的值由參數(shù) name 傳入,現(xiàn)在就是"wangguniang"。

在類 Person 的其它方法中,都是以 self 為第一個(gè)或者唯一一個(gè)參數(shù)。注意,在 Python 中,這個(gè)參數(shù)要顯明寫上,在類內(nèi)部是不能省略的。這就表示所有方法都承接 self 實(shí)例對(duì)象,它的屬性也被帶到每個(gè)方法之中。例如在方法里面使用 self.name 即是調(diào)用前面已經(jīng)確定的實(shí)例屬性數(shù)據(jù)。當(dāng)然,在方法中,還可以繼續(xù)為實(shí)例 self 增加屬性,比如 self.breast。這樣,通過(guò) self 實(shí)例,就實(shí)現(xiàn)了數(shù)據(jù)在類內(nèi)部的流轉(zhuǎn)。

如果要把數(shù)據(jù)從類里面?zhèn)鞯酵饷?,可以通過(guò) return 語(yǔ)句實(shí)現(xiàn)。如上例子中所示的 getName 方法。

因?yàn)閷?shí)例名稱(girl)和 self 是對(duì)應(yīng)關(guān)系,實(shí)際上,在類里面也可以用 girl 代替 self。例如,做如下修改:

#!/usr/bin/env Python
# coding=utf-8

__metaclass__ = type

class Person:
    def __init__(self, name):
        self.name = name

    def getName(self):
        #return self.name
        return girl.name    #修改成這個(gè)樣子,但是在編程實(shí)踐中不要這么做。

girl = Person('wangguniang')
name = girl.getName()
print name

運(yùn)行之后,打?。?/p>

wangguniang

這個(gè)例子說(shuō)明,在實(shí)例化之后,實(shí)例變量 girl 和函數(shù)里面的那個(gè) self 實(shí)例是完全對(duì)應(yīng)的。但是,提醒讀者,千萬(wàn)不要用上面的修改了的那個(gè)方式。因?yàn)槟菢訉懯诡悰]有獨(dú)立性,這是大忌。

命名空間

命名空間,英文名字:namespaces。在研究類或者面向?qū)ο缶幊讨?,它常常被提到。雖然在《函數(shù)(2)中已經(jīng)對(duì)命名空間進(jìn)行了解釋,那時(shí)是在函數(shù)的知識(shí)范疇中對(duì)命名空間的理解。現(xiàn)在,我們?cè)陬惖闹R(shí)范疇中理解“類命名空間”——定義類時(shí),所有位于 class 語(yǔ)句中的代碼都在某個(gè)命名空間中執(zhí)行,即類命名空間。

在研習(xí)命名空間以前,請(qǐng)打開在 Python 的交互模式下,輸入:import this,可以看到:

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

這里列位看到的就是所謂《Python 之禪》,請(qǐng)看最后一句: Namespaces are one honking great idea -- let's do more of those!

這是為了向看官說(shuō)明 Namespaces、命名空間值重要性。

把在《函數(shù)(2)》中已經(jīng)闡述的命名空間用一句比較學(xué)術(shù)化的語(yǔ)言概括:

命名空間是從所定義的命名到對(duì)象的映射集合。

不同的命名空間,可以同時(shí)存在,當(dāng)彼此相互獨(dú)立互不干擾。

命名空間因?yàn)閷?duì)象的不同,也有所區(qū)別,可以分為如下幾種:

  • 內(nèi)置命名空間(Built-in Namespaces):Python 運(yùn)行起來(lái),它們就存在了。內(nèi)置函數(shù)的命名空間都屬于內(nèi)置命名空間,所以,我們可以在任何程序中直接運(yùn)行它們,比如前面的 id(),不需要做什么操作,拿過(guò)來(lái)就直接使用了。
  • 全局命名空間(Module:Global Namespaces):每個(gè)模塊創(chuàng)建它自己所擁有的全局命名空間,不同模塊的全局命名空間彼此獨(dú)立,不同模塊中相同名稱的命名空間,也會(huì)因?yàn)槟K的不同而不相互干擾。
  • 本地命名空間(Function&Class: Local Namespaces):模塊中有函數(shù)或者類,每個(gè)函數(shù)或者類所定義的命名空間就是本地命名空間。如果函數(shù)返回了結(jié)果或者拋出異常,則本地命名空間也結(jié)束了。

從網(wǎng)上盜取了一張圖,展示一下上述三種命名空間的關(guān)系

http://wiki.jikexueyuan.com/project/start-learning-python/images/20803.png" alt="" />

那么程序在查詢上述三種命名空間的時(shí)候,就按照從里到外的順序,即:Local Namespaces --> Global Namesspaces --> Built-in Namesspaces

>>> def foo(num,str):
...     name = "qiwsir"
...     print locals()
... 
>>> foo(221,"qiwsir.github.io")
{'num': 221, 'name': 'qiwsir', 'str': 'qiwsir.github.io'}
>>> 

這是一個(gè)訪問(wèn)本地命名空間的方法,用 print locals() 完成,從這個(gè)結(jié)果中不難看出,所謂的命名空間中的數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)和 dictionary 是一樣的。

根據(jù)習(xí)慣,看官估計(jì)已經(jīng)猜測(cè)到了,如果訪問(wèn)全局命名空間,可以使用 print globals()。

作用域

作用域是指 Python 程序可以直接訪問(wèn)到的命名空間?!爸苯釉L問(wèn)”在這里意味著訪問(wèn)命名空間中的命名時(shí)無(wú)需加入附加的修飾符。(這句話是從網(wǎng)上抄來(lái)的)

程序也是按照搜索命名空間的順序,搜索相應(yīng)空間的能夠訪問(wèn)到的作用域。

def outer_foo():
    b = 20
    def inner_foo():
        c = 30
a = 10

假如我現(xiàn)在位于 inner_foo() 函數(shù)內(nèi),那么 c 對(duì)我來(lái)講就在本地作用域,而 b 和 a 就不是。如果我在 inner_foo() 內(nèi)再做:b=50,這其實(shí)是在本地命名空間內(nèi)新創(chuàng)建了對(duì)象,和上一層中的 b=20 毫不相干??梢钥聪旅娴睦樱?/p>

#!/usr/bin/env Python
#coding:utf-8

def outer_foo():
    a = 10
    def inner_foo():
        a = 20
        print "inner_foo,a=",a      #a=20

    inner_foo()
    print "outer_foo,a=",a          #a=10

a = 30
outer_foo()
print "a=",a                #a=30

#運(yùn)行結(jié)果

inner_foo,a= 20
outer_foo,a= 10
a= 30

如果要將某個(gè)變量在任何地方都使用,且能夠關(guān)聯(lián),那么在函數(shù)內(nèi)就使用 global 聲明,其實(shí)就是曾經(jīng)講過(guò)的全局變量。


總目錄   |   上節(jié):類(2)   |   下節(jié):類(4)

如果你認(rèn)為有必要打賞我,請(qǐng)通過(guò)支付寶:qiwsir@126.com,不勝感激。

上一篇:運(yùn)算符下一篇:函數(shù)(1)