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

類(5)

在前面幾節(jié)討論類的時(shí)候,經(jīng)常要將類實(shí)例化,然后通過實(shí)例來調(diào)用類的方法(函數(shù))。在此,把前面經(jīng)常做的這類事情概括一下:

  • 方法是類內(nèi)部定義函數(shù),只不過這個(gè)函數(shù)的第一個(gè)參數(shù)是 self。(可以認(rèn)為方法是類屬性,但不是實(shí)例屬性)
  • 必須將類實(shí)例化之后,才能通過實(shí)例調(diào)用該類的方法。調(diào)用的時(shí)候在方法后面要跟括號(括號中默認(rèn)有 self 參數(shù),但是不寫出來。)

通過實(shí)例調(diào)用方法(在前面曾用了一個(gè)不嚴(yán)謹(jǐn)?shù)脑~語:實(shí)例方法),我們稱這個(gè)方法綁定在實(shí)例上。

調(diào)用綁定方法

前面一直在這樣做。比如:

class Person(object):
    def foo(self):
        pass

如果要調(diào)用 Person.foo() 方法,必須:

pp = Person()    #實(shí)例化
pp.foo()

這樣就實(shí)現(xiàn)了方法和實(shí)例的綁定,于是通過 pp.foo() 即可調(diào)用該方法。

調(diào)用非綁定方法

《類(4)》中,介紹了一個(gè)函數(shù) super。為了描述方便,把代碼復(fù)制過來:

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

__metaclass__ = type

class Person:
    def __init__(self):
        self.height = 160

    def about(self, name):
        print "{} is about {}".format(name, self.height)

class Girl(Person):
    def __init__(self):
        super(Girl, self).__init__()
        self.breast = 90

    def about(self, name):
        print "{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)
        super(Girl, self).about(name)

if __name__ == "__main__":
    cang = Girl()
    cang.about("wangguniang")

在子類 Girl 中,因?yàn)橹貙懥烁割惖?code>__init__方法,如果要調(diào)用父類該方法,在上節(jié)中不得不使用 super(Girl, self).__init__()調(diào)用父類中因?yàn)樽宇惙椒ㄖ貙懚徽诒蔚耐椒ā?/p>

其實(shí),在子類中,父類的方法就是非綁定方法,因?yàn)樵谧宇愔?,沒有建立父類的實(shí)例,卻要是用父類的方法。對于這種非綁定方法的調(diào)用,還有一種方式。不過這種方式現(xiàn)在已經(jīng)較少是用了,因?yàn)橛辛?super 函數(shù)。為了方便讀者看其它有關(guān)代碼,還是要簡要說明。

例如在上面代碼中,在類 Girl 中想調(diào)用父類 Person 的初始化函數(shù),則需要在子類中,寫上這么一行:

Person.__init__(self)

這不是通過實(shí)例調(diào)用的,而是通過類 Person 實(shí)現(xiàn)了對__init__(self)的調(diào)用。這就是調(diào)用非綁定方法的用途。但是,這種方法已經(jīng)被 super 函數(shù)取代,所以,如果讀者在編程中遇到類似情況,推薦使用 super 函數(shù)。

靜態(tài)方法和類方法

已知,類的方法第一個(gè)參數(shù)必須是 self,并且如果要調(diào)用類的方法,必須將通過類的實(shí)例,即方法綁定實(shí)例后才能由實(shí)例調(diào)用。如果不綁定,一般在繼承關(guān)系的類之間,可以用 super 函數(shù)等方法調(diào)用。

這里再介紹一種方法,這種方法的調(diào)用方式跟上述的都不同,這就是:靜態(tài)方法和類方法。看代碼:

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

__metaclass__ = type

class StaticMethod:
    @staticmethod
    def foo():
        print "This is static method foo()."

class ClassMethod:
    @classmethod
    def bar(cls):
        print "This is class method bar()."
        print "bar() is part of class:", cls.__name__

if __name__ == "__main__":
    static_foo = StaticMethod()    #實(shí)例化
    static_foo.foo()               #實(shí)例調(diào)用靜態(tài)方法
    StaticMethod.foo()             #通過類來調(diào)用靜態(tài)方法
    print "********"
    class_bar = ClassMethod()
    class_bar.bar()
    ClassMethod.bar()

對于這部分代碼,有一處非常特別,那就是包含了“@”符號。在 Python 中:

  • @staticmethod表示下面的方法是靜態(tài)方法
  • @classmethod表示下面的方法是類方法

一個(gè)一個(gè)來看。

先看靜態(tài)方法,雖然名為靜態(tài)方法,但也是方法,所以,依然用 def 語句來定義。需要注意的是文件名后面的括號內(nèi),沒有 self,這和前面定義的類中的方法是不同的,也正是因著這個(gè)不同,才給它另外取了一個(gè)名字叫做靜態(tài)方法,否則不就“泯然眾人矣”。如果沒有 self,那么也就無法訪問實(shí)例變量、類和實(shí)例的屬性了,因?yàn)樗鼈兌际墙柚?self 來傳遞數(shù)據(jù)的。

在看類方法,同樣也具有一般方法的特點(diǎn),區(qū)別也在參數(shù)上。類方法的參數(shù)也沒有 self,但是必須有 cls 這個(gè)參數(shù)。在類方法中,能夠方法類屬性,但是不能訪問實(shí)例屬性(讀者可以自行設(shè)計(jì)代碼檢驗(yàn)之)。

簡要明確兩種方法。下面看調(diào)用方法。兩種方法都可以通過實(shí)例調(diào)用,即綁定實(shí)例。也可以通過類來調(diào)用,即 StaticMethod.foo() 這樣的形式,這也是區(qū)別一般方法的地方,一般方法必須用通過綁定實(shí)例調(diào)用。

上述代碼運(yùn)行結(jié)果:

$ python 21001.py 
This is static method foo().
This is static method foo().
********
This is class method bar().
bar() is part of class: ClassMethod
This is class method bar().
bar() is part of class: ClassMethod

這是關(guān)于靜態(tài)方法和類方法的簡要介紹。

正當(dāng)我思考如何講解的更深入一點(diǎn)的時(shí)候,我想起了以往看過的一篇文章,覺得人家講的非常到位。所以,不敢吝嗇,更不敢班門弄斧,所以干醋把那篇文章恭恭敬敬的抄錄于此。同時(shí),讀者從下面的文章中,也能對前面的知識復(fù)習(xí)一下。文章標(biāo)題是:Python 中的 staticmethod 和 classmethod 的差異。原載:www.pythoncentral.io/difference-between-staticmethod-and-classmethod-in-Python/。此地址需要你準(zhǔn)備梯子才能瀏覽。后經(jīng)國人翻譯,地址是:http://www.wklken.me/posts/2013/12/22/difference-between-staticmethod-and-classmethod-in-Python.html

以下是翻譯文章:

Class vs static methods in Python

這篇文章試圖解釋:什么事 staticmethod/classmethod,并且這兩者之間的差異.

staticmethod 和 classmethod 均被作為裝飾器,用作定義一個(gè)函數(shù)為"staticmethod"還是"classmethod"

如果想要了解 Python 裝飾器的基礎(chǔ),可以看這篇文章

Simple, static and class methods

類中最常用到的方法是 實(shí)例方法(instance methods), 即,實(shí)例對象作為第一個(gè)參數(shù)傳遞給函數(shù)

例如,下面是一個(gè)基本的實(shí)例方法

class Kls(object):
    def __init__(self, data):
        self.data = data

    def printd(self):
        print(self.data)

ik1 = Kls('arun')
ik2 = Kls('seema')

ik1.printd()
ik2.printd()

得到的輸出:

arun
seema

調(diào)用關(guān)系圖:

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

查看代碼和圖解:

1/2 參數(shù)傳遞給函數(shù)

3 self 參數(shù)指向?qū)嵗旧?

4 我們不需要顯式提供實(shí)例,解釋器本身會處理

假如我們想僅實(shí)現(xiàn)類之間交互而不是通過實(shí)例?我們可以在類之外建立一個(gè)簡單的函數(shù)來實(shí)現(xiàn)這個(gè)功能,但是將會使代碼擴(kuò)散到類之外,這個(gè)可能對未來代碼維護(hù)帶來問題。

例如:

def get_no_of_instances(cls_obj):
    return cls_obj.no_inst

class Kls(object):
    no_inst = 0

    def __init__(self):
        Kls.no_inst = Kls.no_inst + 1

ik1 = Kls()
ik2 = Kls()

print(get_no_of_instances(Kls))

結(jié)果:

2

The Python @classmethod

現(xiàn)在我們要做的是在類里創(chuàng)建一個(gè)函數(shù),這個(gè)函數(shù)參數(shù)是類對象而不是實(shí)例對象.

在上面那個(gè)實(shí)現(xiàn)中,如果要實(shí)現(xiàn)不獲取實(shí)例,需要修改如下:

def iget_no_of_instance(ins_obj):
    return ins_obj.__class__.no_inst

class Kls(object):
    no_inst = 0

    def __init__(self):
        Kls.no_inst = Kls.no_inst + 1

ik1 = Kls()
ik2 = Kls()
print iget_no_of_instance(ik1)

結(jié)果

2

可以使用 Python2.2 引入的新特性,使用 @classmethod 在類代碼中創(chuàng)建一個(gè)函數(shù)

class Kls(object):
    no_inst = 0

    def __init__(self):
        Kls.no_inst = Kls.no_inst + 1

    @classmethod
    def get_no_of_instance(cls_obj):
        return cls_obj.no_inst

ik1 = Kls()
ik2 = Kls()

print ik1.get_no_of_instance()
print Kls.get_no_of_instance()

We get the following output:

2
2

The Python @staticmethod

通常,有很多情況下一些函數(shù)與類相關(guān),但不需要任何類或?qū)嵗兞烤涂梢詫?shí)現(xiàn)一些功能.

比如設(shè)置環(huán)境變量,修改另一個(gè)類的屬性等等.這種情況下,我們也可以使用一個(gè)函數(shù),一樣會將代碼擴(kuò)散到類之外(難以維護(hù))

下面是一個(gè)例子:

IND = 'ON'

def checkind():
    return (IND == 'ON')

class Kls(object):
    def __init__(self,data):
        self.data = data

    def do_reset(self):
        if checkind():
            print('Reset done for:', self.data)

    def set_db(self):
        if checkind():
            self.db = 'new db connection'
            print('DB connection made for:',self.data)

ik1 = Kls(12)
ik1.do_reset()
ik1.set_db()

結(jié)果:

Reset done for: 12
DB connection made for: 12

現(xiàn)在我們使用 @staticmethod, 我們可以將所有代碼放到類中

IND = 'ON'

class Kls(object):
    def __init__(self, data):
        self.data = data

    @staticmethod
    def checkind():
        return (IND == 'ON')

    def do_reset(self):
        if self.checkind():
            print('Reset done for:', self.data)

    def set_db(self):
        if self.checkind():
            self.db = 'New db connection'
        print('DB connection made for: ', self.data)

ik1 = Kls(12)
ik1.do_reset()
ik1.set_db()

得到的結(jié)果:

Reset done for: 12
DB connection made for: 12

How @staticmethod and @classmethod are different

class Kls(object):
    def __init__(self, data):
        self.data = data

    def printd(self):
        print(self.data)

    @staticmethod
    def smethod(*arg):
        print('Static:', arg)

    @classmethod
    def cmethod(*arg):
        print('Class:', arg)

調(diào)用

>>> ik = Kls(23)
>>> ik.printd()
23
>>> ik.smethod()
Static: ()
>>> ik.cmethod()
Class: (<class '__main__.Kls'>,)
>>> Kls.printd()
TypeError: unbound method printd() must be called with Kls instance as first argument (got nothing instead)
>>> Kls.smethod()
Static: ()
>>> Kls.cmethod()
Class: (<class '__main__.Kls'>,)

圖解

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

文檔字符串

在寫程序的時(shí)候,必須要寫必要的文字說明,沒別的原因,除非你的代碼寫的非常容易理解,特別是各種變量、函數(shù)和類等的命名任何人都能夠很容易理解,否則,文字說明是不可缺少的。

在函數(shù)、類或者文件開頭的部分寫文檔字符串說明,一般采用三重引號。這樣寫的最大好處是能夠用 help() 函數(shù)看。

"""This is python lesson"""

def start_func(arg):
    """This is a function."""
    pass

class MyClass:
    """Thi is my class."""
    def my_method(self,arg):
        """This is my method."""
        pass

這樣的文檔是必須的。

當(dāng)然,在編程中,有不少地方要用“#”符號來做注釋。一般用這個(gè)來注釋局部。


總目錄   |   上節(jié):類(4)   |   下節(jié):多態(tài)和封裝

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

下一篇:分析 Hello