1. 程式人生 > 實用技巧 >Python中的函式和方法

Python中的函式和方法

在Python中,對函式和方法都有明確的規定:

函式function---A series of statements which returns some value to a caller. It can also be passed zero or more arguments which may be used in the execution of the body.

方法method---A function which is defined inside a class body. If called as an attribute of an instance of that class, the method will get the instance object as its first argument(which is usually called self).

Python中類的方法

類中的方法,可以分為:例項方法,類方法,靜態方法。方法和欄位一樣,也是屬於類的屬性。

例項方法

例項方法就是類的例項能夠使用的方法。

>>> class Foo:
    def __init__(self, name):
        self.name = name
    def hi(self):
        print self.name


>>> foo01 = Foo('letian')
>>> foo01.hi()
letian
>>> print type(Foo)
<type 'classobj'>
>>> print type(foo01)
<type 'instance'>
>>> print id(foo01)
47516040
>>> print id(Foo)
51151112
>>>

構造器__init__就是一個例項方法,特點是:

1.方法的第一個引數必須是self,當然這是約定俗成的寫法,可以將self換成abc之類的,但是為了別的程式設計師能看得懂,還是統一用self吧。這裡的self代表例項本身,也就是說如果我例項化時使用的是:a = Test(),那麼self就代表a這個例項。我們可以在很多構造器中看到類似self.scolia = 'good'的寫法,其實這個寫法和在類外面a.scolia = 'good'效果是一樣的,都是為了新增屬性,只不過__init__方法是例項化時自動呼叫的函式,所以適合進行初始屬性的建立。

2.例項方法在呼叫的時候,self是自動傳遞的,所以不需要我們再處理;

3.例項方法一般要有例項才能呼叫,當然也有特殊的呼叫方法。

class Test(object):

    def __init__(self, a, b):   # 構造器在例項建立時進行屬性的初始化
        self.a = int(a)
        self.b = int(b)

    def abc(self, c):  # 例項方法
        print self.a + self.b + int(c)  # 因為self是自動傳遞的,所以我們可以在例項方法中呼叫例項的屬性

a = Test(123, 321)  # 我們只要為 a 和 b 傳參就行了
a.abc(666)  # 同樣的,只要為 c 傳參

>>>1110

這裡,將引入一個繫結(binding)的概念,其主要和方法的呼叫有關係。

這裡,將引入一個繫結(binding)的概念,其主要和方法的呼叫有關係。
首先,我們知道方法是類的屬性,而不是例項的屬性。其次,方法只有在其所屬的類擁有例項時,才能被呼叫。當一個類存在例項後,方法才被認為是繫結到這個例項。沒有例項的時候,方法是未繫結的。最後,任何一個方法定義的第一個引數都是變數self,它表示呼叫此方法的例項物件。
很明顯這裡的繫結針對的是例項方法,因為如果沒有例項的話,self就無法傳遞,這將導致引數的不足,當然就無法呼叫了。

但是,可以自己傳遞self來呼叫未繫結的方法。呼叫未繫結的方法通常是在我們繼承了一個父類後,我們覆蓋了父類中的某個方法,但是為了實現程式碼重用,我們又想在子類中呼叫父類的方法。單純的複製父類中的程式碼明顯不是一個好選擇,除了浪費系統資源之外,還有可能在複製的時候出錯,而且以後修改父類的程式碼之後,還要修改相應子類中的程式碼,實在太低效,這個時候就是呼叫未繫結方法的場景。

class abc(object):
    def __init__(self, a):
        self.a = -int(a)

class Test(abc):
    def __init__(self, a, b):
        abc.__init__(self, a)   # 呼叫父類的構造器,並手動傳遞 self
        self.b = b

    def fangfa(self):
        print self.a + self.b   # 屬性 a 由父類的構造器建立,b 由子類構造器建立

a = Test(123, 321)  # 我們只建立了子類的例項,而沒有建立父類的例項
a.fangfa()

>>>198

本來我們沒有建立父類的例項,是無法呼叫父類的例項方法的,但是我們手動傳遞了例項方法所需要的self引數,就可以實現呼叫了。
這裡的順序是,我們建立了Test的例項a,其self是自動傳遞的,故Test的構造方法__init__(self, a, b)中self就代表例項a,而我們又呼叫了父類的abc.__init__(self, a)這裡的self就是子類的例項a,引數a就是我們傳的123,而父類中self.a = -int(a),最後我們可以在子類的方法中呼叫self.a這個屬性。

類方法

類方法和例項方法類似,不過其第一個引數一般是cls(約定俗成)而不是self。但是,如果我們直接將self換成cls來建立類方法是不對的,因為例項方法的首個引數也是任意的,只是統一使用self。Python的直譯器並沒有說看見第一個引數是cls就知道這個是類方法,它還是將其當做是例項方法來對待,所有我們需要通過內建函式:classmethod()來建立類方法。

class Test(object):

    def abc(cls):
        print cls.__name__  # 列印類名

    abc = classmethod(abc)  # 通過普通的函式傳參的方式建立類方法

a = Test()  
Test.abc()  # 類能呼叫
a.abc()     # 例項也能呼叫

>>>Test
>>>Test

當然,也可以使用裝飾器的方法來建立類方法:

class Test(object):
    @classmethod
    def abc(cls):
        print cls.__name__  # 列印類名

結果也是一樣的,具體選擇看個人喜好。注意,因為使用了classmethod()函式,其第一個引數cls是固定傳遞的,而且是代表當前的類。並沒有例項方法中非繫結方法的呼叫形式。

靜態方法

靜態方法其實就是類中的一個普通函式,就位於類定義的名稱空間中,它不會對任何例項進行操作,類和例項都可以呼叫靜態方法。它並沒有預設傳遞的引數,在建立靜態方法的時候,需要用到內建函式:staticmethod()。使用靜態方法的好處是,不需要定義例項即可使用這個方法。另外,多個例項共享此靜態方法。

class Test(object):

    def abc():
        print 'abc'

    abc = staticmethod(abc)

    @staticmethod
    def xyz(a, b):
        print a + b

Test.abc()  # 類呼叫
Test.xyz(1, 2)  # 類呼叫
a = Test()
a.abc()     # 例項呼叫
a.xyz(3, 4)  # 例項呼叫

>>>abc
>>>3
>>>abc
>>>7

注意,雖然靜態方法沒有預設引數,但並不代表不能有引數。

用方法冒充欄位

有時候,一個方法在經過一系列處理以後,返回的是一個數據,例如:

class Test:

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def fangfa(self):
        c = self.a + self.b
        return c    # 返回處理的結果資料


a = Test(1, 2)
b = a.fangfa()    # 呼叫方法,得到返回值
print b

>>>3

但是,懶惰的程式設計師們想,我想要的只是和欄位類似的資料,卻要呼叫一個方法,有時容易搞錯,能不能用欄位的形式獲取資料呢?這樣更加符合直覺。
可以,只有使用property()函式就可以了。同樣的,這裡也有兩種建立方式,這裡只演示裝飾器的方法。

class Test:

    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def fangfa(self):
        c = self.a + self.b
        return c


a = Test(1, 2)
b = a.fangfa    # 不用帶括號了
print b

這樣是實現了方法偽裝成欄位了,其實就是懶惰的程式設計師們不願意多寫一個括號,當然還有一些其他好處。
另外,函式要有返回值,不然就預設為None了。
如果在經典類中,我們就只能做到這樣了。
但是,使用新式類的話,就能有更多的功能:

class Test(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def fangfa(self):
        c = self.a + self.b
        return c

    @fangfa.setter
    def fangfa(self, value):
        self.a = value

    @fangfa.deleter
    def fangfa(self):
        print '屬性已刪除'

a = Test(1, 2)
b = a.fangfa   # 獲得方法的返回值
print b
a.fangfa = 100    # 執行 fangfa.setter 修飾的方法,並讓value = 100
print a.a
del a.fangfa    # 執行 fangfa.deleter 修飾的方法

>>>3
>>>100
屬性已刪除

注意後面兩個裝飾的名字。
另外,方法必須要先經過property()函式的裝飾後,才有後面兩個裝飾器的用法。

如果使用非裝飾器的形式的話,

class Test(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def fangfa_get(self):
        c = self.a + self.b
        return c

    def fangfa_set(self, value):
        self.a = value

    def fangfa_del(self):
        print '屬性已刪除'

    fangfa = property(fangfa_get, fangfa_set, fangfa_del)
property(fget=None, fset=None, fdel=None, doc=None

最後的doc是說明文件,看個人需要新增,可以通過Test.fanda.__doc__進行訪問。

Super

super用來執行父類中的方法。在類的繼承中,如果重定義某個方法,該方法會覆蓋父類的同名方法,但有時,我們希望能同時實現父類的功能,這時,我們就需要呼叫父類的方法了,可以通過使用super來實現。

class Animal(object):
    def __init__(self, name):
        self.name = name
    def greet(self):
        print 'Hello, I am %s.' % self.name

class Dog(Animal):
    def greet(self):
        super(Dog, self).greet()   # Python3 可使用 super().greet()
        print 'WangWang...'

>>> dog = Dog('dog')
>>> dog.greet()
Hello, I am dog.
WangWang..
super(type[, object-or-type])
Return the superclass of type. If the second argument is omitted the super object returned is unbound. If the second argument is an object, isintance(obj,type) must be true. If the second argument is a type, issubclass(type2, type) must be true, super() only works for new-style classes.
A typical use for calling a cooperative superclass method is:
class C(B):
  def meth(self, arg):
    super(C, self).meth(arg)
class Foo(object):
    def hi(self):
        print 'hi,Foo'
class Foo2(Foo):
    def hi(self):
        super(Foo2, self).hi()
if __name__ == '__main__':
    foo2 = Foo2()
    foo2.hi()

>>>hi, Foo

注意,Foo類必須繼承某個類(並且這個繼承鏈開始於object類),否則會報錯。如果改成下面的形式:

class Foo:
    def hi(self):
        print 'hi,Foo'
class Foo2(Foo):
    def hi(self):
        super(Foo2, self).hi()
if __name__ == '__main__':
    foo2 = Foo2()
    foo2.hi()

執行結果如下,
Traceback (most recent call last):
  File "<pyshell#17>", line 1, in <module>
    foo2.hi()
  File "<pyshell#15>", line 3, in hi
    super(Foo2, self).hi()
TypeError: must be type, not classobj

super 的一個最常見用法可以說是在子類中呼叫父類的初始化方法了,比如:

class Base(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

class A(Base):
    def __init__(self, a, b, c):
        super(A, self).__init__(a, b)  # Python3 可使用 super().__init__(a, b)
        self.c = c

MRO列表

事實上,對於你定義的每一個類,Python都會計算出一個方法解析順序(Method Resolution Order, MRO)列表,它代表了類繼承的順序,我們可以使用下面的方式獲得某個類的MRO列表:

>>> C.mro()   # or C.__mro__ or C().__class__.mro()
[__main__.C, __main__.A, __main__.B, __main__.Base, object]

一個類的MRO列表就是合併所有父類的MRO列表,並遵循以下三條原則:

1.子類永遠在父類前面;

2.如果有多個父類,會根據它們在列表中的順序被檢查;

3.如果對下一個類存在兩個合法的選擇,選擇第一個父類;

Super原理

super的工作原理如下:

def super(cls, inst):
  mro = inst.__class__.mro()
  return mro[mro.index(cls) + 1]

其中,cls代表類,inst代表例項,上面的程式碼做了兩件事:

1. 獲取inst的MRO列表;

2. 查詢cls在當前MRO列表中的index,並返回它的下一個類,即mro[index+1];

當你使用super(cls, inst)時,Python會在inst的MRO列表上搜索cls的下一個類。

現在,讓我們回到前面的例子。

首先,看類C的__init__方法:

super(C, self).__init__()

這裡的self是當前C的例項,self.__class__.mro()結果是:

[__main__.C, __main__.A, __main__.B, __main__.Base, object]

可以看到,C的下一個類是A,於是,跳到了A的__init__,這是會打印出enter A,並執行下面一行程式碼:

super(A, self).__init__()

注意,這裡的self也是當前C的例項,MRO列表跟上面是一樣的,搜尋A在MRO列表中的下一個類,發現是B,於是,跳到了B的__init__,這時會打印出enter B,而不是enter Base。

整個過程還是比較清晰的,關鍵是要理解 super 的工作方式,而不是想當然地認為 super 呼叫了父類的方法。
事實上,super和父類沒有實質性的關聯,super(cls, inst)獲得的是cls在inst的MRO列表中的下一個類。

Python中repr和str的區別

repr語法: repr[object]

返回一個可以表示物件的可列印的字串。首先,嘗試生成一個這樣的字串,然後將其傳遞給eval(),可以重新生成同樣的物件。否則,生成用尖括號包住的字串,包含型別名稱和額外的資訊(比如地址)。一個類(class)可以通過__repr__()成員來控制repr()函式作用在其例項上時的行為。但是repr所返回的物件更適合於直譯器去閱讀,可以理解為親近python。

Return a string containing a printable representation of an object. This is the save value yielded by conversions(reverse quotes). It is sometimes useful to be able to access this operation as an ordinary function. For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(), otherwise the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object. A class can control what this function returns for its instances by defining a repr() method.

str語法: str[object]

返回一個可以表示物件的友好的可列印的字串。對於字串則返回本身,如果沒有引數,則返回空字串。對類,可以通過__str__()成員控制其行為,該成員不存在,則使用其__repr__()成員。str返回的物件更合人類閱讀,str致力於返回一個可讀性比較好的物件,返回的結構通常不會通過eval()去處理。與repr的區別:不總是嘗試生成一個傳給eval的字串,其目標是可列印字串。

Return a string containing a nicely printable representation of an object. For strings, this returns the string itself. The difference with repr(object) is that str(object) does not always attempt to return a string that is acceptable to eval(); its goal is to return a printable string. If no argument is given, returns the empty string.
The str() function is meant to return representations of values which are fairly human-readable.
While repr() is meant to generate representations which can be read by the interpreter.
For objects which don't have a particular representation for human consumption, str() will return the same value as repr().
Many values, such as numbers or structures like lists and dictionaries, have the same representation using either function. Strings and float point numbers, in particular, have two distinct representations.

看下面的例子就明白了

class Test(object):
  def __init__(self, value='hello, world!'):
    self.data = value

>>>t = Test()
>>>t
<__main__.Test at 0x7fa91c307190>
>>>print t
<__main__.Test object at 0x7fa91c307190>

看到了麼?上面列印類物件並不是很友好,顯示的是物件的記憶體地址。
下面我們重構下該類的__repr__以及__str__,看看它們倆有啥區別。

重構__repr__

class TestRepr(Test):
  def __repr__(self):
    return 'TestRepr(%s)' %self.data

>>>tr = TestRepr()
>>>tr
TestRepr(hello,world!)
>>>print tr
TestRepr(hello,world!)

重構__repr__方法後,不管直接輸出物件還是通過print列印的資訊都按照我們__repr__方法中定義的格式進行顯示了。

重構__str__

class TestStr(Test):
  def __str__(self):
    return '[Value: %s]' %self.data
>>>ts = TestStr()
>>>ts
<__main__.TestStr at 0x7fa91c314e50>
>>>print ts
[Value: hello, world!]

你會發現,直接輸出物件ts時並沒有按照我們__str__方法中定義的格式進行輸出,而用print輸出的資訊卻改變了。
__repr____str__這兩個方法都是用於顯示的,__str__是面向使用者的,而__repr__面向程式設計師。

1.列印操作會首先嚐試__str__str(print執行的內部等價形式)內建函式,它通常應該返回一個友好的顯示。

2.__repr__用於所有其他的環境中:用於互動模式下提示迴應以及repr函式,如果沒有使用__str__,會使用printstr,它通常應該返回一個編碼字串,可以用來重新建立物件,或者給開發者詳細的顯示。
當我們想所有環境下都統一顯示的話,可以重構__repr__方法;當我們想在不同環境下支援不同的顯示,例如終端使用者顯示使用__str__,而程式設計師在開發期間則使用底層的__repr__來顯示,實際上__str__只是覆蓋了__repr__以得到更友好的使用者顯示。

eval函式

將字串str當成有效的表示式來求值並返回計算結果。

語法: eval(source[, globals[,locals]]) - > value
引數:
source: 一個Python表示式或函式compile()返回的程式碼物件;
globals: 可選。必須是dictionary。
locals: 可選。任意map物件。

可以把list,tuple,dict和string相互轉化。
#################################################
#字串轉換成列表
>>>a = "[[1,2], [3,4], [5,6], [7,8], [9,0]]"
>>>type(a)
<type 'str'>
>>> b = eval(a)
>>> print b
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 0]]
>>> type(b)
<type 'list'>
#################################################
#字串轉換成字典
>>> a = "{1: 'a', 2: 'b'}"
>>> type(a)
<type 'str'>
>>> b = eval(a)
>>> print b
{1: 'a', 2: 'b'}
>>> type(b)
<type 'dict'>
#################################################
#字串轉換成元組
>>> a = "([1,2], [3,4], [5,6], [7,8], (9,0))"
>>> type(a)
<type 'str'>
>>> b = eval(a)
>>> print b
([1, 2], [3, 4], [5, 6], [7, 8], (9, 0))
>>> type(b)
<type 'tuple'>

但是強大的函式有代價,安全性是其最大的缺點。
想一想這種使用環境:需要使用者輸入一個表示式,並求值。
如果使用者惡意輸入,例如:

__import__('os').system('dir')

那麼eval()之後,你會發現,當前目錄檔案都會展現在使用者前面。
那麼繼續輸入:

open('檔名').read()

程式碼都給人看了,獲取完畢,一條刪除命令,檔案消失。
eval函式在Python中做資料型別轉換還是很有用的。它的作用就是把資料還原成它本身或者是能夠轉化成的資料型別。
那麼eval和ast.literal_val()的區別是什麼呢?
eval在做計算前並不知道需要轉化的內容是不是合法的(安全的)python資料型別。只是在呼叫函式的時候去計算,如果被計算的內容不是合法的python型別就會丟擲異常。
ast.literal_eval則會判斷需要計算的內容,計算後是不是合法的python型別,如果是,則進行運算,否則就不進行運算。

datamap = eval(raw_input('Provide some data here:')) means that you actually evaluate the code before you deem it to be unsafe or not.It evaluates the code as soon as the function is called. See also the dangers of eval.
ast.literal_eval raises an exception if the input isn't valid python datatype, so the code won't be executed if it's not.
Use ast.literal_eval whenever you need eval. If you have Python expressions as an input that you want to evaluate, you should not(have them).

unbound method和bound method

>>> class C(object):
    def foo(self):
        pass


>>> C.foo
<unbound method C.foo>
>>> C().foo
<bound method C.foo of <__main__.C object at 0x0000000002A1C9E8>>
>>> class A():
    def foo(self):
        pass


>>> A.foo
<unbound method A.foo>
>>> A().foo
<bound method A.foo of <__main__.A instance at 0x00000000029C5B88>>
>>>

為什麼C.foo是一個unbound method,C().foo是一個bound method?Python為什麼這樣設計?
如果明白Python中描述器(descriptor)是怎麼實現的,方法(method)是很容易理解的。
上例程式碼中可以看到,如果你用類C去訪問foo方法,會得到unbound方法,然而在class的內部儲存中它是個function,為什麼?原因就是C的類(注意是類的類)實現了一個__getattribute__來解析描述器。聽起來複雜,但並非如此。上例中的C.foo等價於:

>>> C.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'C' objects>, '__module__': '__main__', 'foo': <function foo at 0x0000000002A1E7B8>, '__weakref__': <attribute '__weakref__' of 'C' objects>, '__doc__': None})
>>> C.__dict__['foo']
<function foo at 0x0000000002A1E7B8>
>>> C.__dict__['foo'].__dict
>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>
>>>

這是因為方法foo有個__get__方法,也就是說,方法是個描述器。如果你用例項來訪問的話也是一模一樣的:

>>> c = C()
>>> C.__dict__['foo'].__get__(c, C)
<bound method C.foo of <__main__.C object at 0x0000000002AF00B8>>
>>>

只是那個None換成了這個例項。現在我們來討論,為什麼Python要這麼設計?
其實,所謂bound method,就是方法物件的第一個函式引數繫結為這個類的例項(所謂bind)。這也是那個self的由來。
當你不想讓類把一個函式作為一個方法,可以使用裝飾器staticmethod。

>>> class C(object):
...     @staticmethod
...     def foo():
...         pass
...
>>> C.foo
<function foo at 0xb76d056c>
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0xb76d056c>

staticmethod裝飾器會讓foo的__get__返回一個函式,而不是一個方法。

function,bound method和unbound method的區別是什麼?

一個函式(function)是由def語句或者lambda建立的。

當一個函式(function)定義在了class語句的塊中(或者由type來建立的),它會轉成一個unbound method,當我們通過一個類的例項來訪問這個函式的時候,它就轉成了bound method,bound method會自動把這個例項作為函式的第一個引數。

所以,bound method就是綁定了一個例項的方法,否則叫做unbound method。它們都是方法(method),是出現在class中的函式。

類變數和例項變數

類變數定義在類的定義中,例項變數則是以self開頭。例項也能夠訪問類變數。

只要宣告在類的語句塊中,且沒有'self'字首的變數都是類變數,且類變數是被所有物件共享的。

如果在類的方法的語句塊中宣告,那麼就是區域性變量了,比如下面這個例子:

>>> class Person:
    cvar = 1
    def sayHi(self):
        fvar = 2
>>> Person.cvar
1
>>> Person.fvar
Traceback (most recent call last):
  File "<pyshell#48>", line 1, in <module>
    Person.fvar
AttributeError: class Person has no attribute 'fvar'

那麼這個cvar就是屬於Python類的變數,而那個fvar就是方法sayHi()中的區域性變數。
在類的語句塊及其中方法的語句塊中宣告的,以'self'開頭的變數都是例項變數,由物件獨有!

>>> class Person:
    def haveName(self):
        self.name = 'Michael'
    def sayName(self):
        print self.name
>>> p=Person()
>>> p.sayName()
Traceback (most recent call last):
  File "<pyshell#59>", line 1, in <module>
    p.sayName()
  File "<pyshell#57>", line 5, in sayName
    print self.name
AttributeError: Person instance has no attribute 'name'
>>> p.haveName()
>>> p.sayName()
Michael
>>>

這裡在haveName()方法中聲明瞭一個例項變數,然後在sayName()方法中呼叫。

不過,建議將例項變數宣告在__init__()方法中,因為物件一被建立的時候即會呼叫這個方法,否則的話,比如上面那個例子,如果我先呼叫sayName()的話,那麼就會出錯,說物件例項還沒有name這個屬性!

類方法中self.xxx和xxx的區別

Python類方法中的內部變數,命名加'self.'變成self.xxx和xxx的區別。

>>> class nc():
          def init(self):
              self.name = 'tester' #name變數加self
>>> class mc():
          def init(self):
              name = 'tester' #name變數不加self
>>> NC = nc()
>>> NC.name
'tester'
>>> nc.name
  Traceback (most recent call last):
  File "<pyshell#86>", line 1, in <module>
    nc.name
  AttributeError: class nc has no attribute 'name'
>>> MC = mc()
>>> MC.name
  Traceback (most recent call last):
  File "<pyshell#88>", line 1, in <module>
    MC.name
  AttributeError: mc instance has no attribute 'name'
>>> mc.name
  Traceback (most recent call last):
  File "<pyshell#89>", line 1, in <module>
    mc.name
  AttributeError: class mc has no attribute 'name'
>>>

python類方法中的變數不加self,也就是xxx,那麼這個是方法的區域性變數。

其它

Python的靜態方法和類方法都可以被類或例項訪問,但是還是有區別的:

1.靜態方法無需傳入self引數,類方法需要傳入代表本類的cls引數;

2.靜態方法是無法訪問例項變數的,而類方法也同樣無法訪問例項變數,但可以訪問類變數;

3.靜態方法有點像函式工具庫的作用。

在Python2.4及之後,用裝飾器(decorators)實現,

class MyClass:
    val1 = 'Value 1'
    def __init__(self):
        self.val2 = 'Value 2'

    @staticmethod
    def staticmd():
        print '靜態方法,無法訪問val1和val2'

    @classmethod
    def classmd(cls):
        print '類方法,類:' + str(cls) + ',val1:' + cls.val1 + ',無法訪問val2的值'

>>> mc = MyClass()  # 例項化
>>> mc.staticmd()  # 例項呼叫靜態方法,無法訪問例項變數val1和val2
>>> 
靜態方法,無法訪問val1和val2

>>> mc.classmd()  # 例項呼叫類方法,注意,這裡訪問的是類MyClass的變數val1的值,不是例項化後mc的例項變數val1,這裡容易混淆,往下看就會明白。val2一直是例項變數,所以無法訪問
>>> 
類方法,類:__main__.MyClass,val1:Value 1,無法訪問val2的值

>>> MyClass.staticmd()  # 類直接呼叫靜態方法,結果同上面的例項呼叫,無論是類變數還是例項變數都無法訪問
>>> 
靜態方法,無法訪問val1和val2

>>> MyClass.classmd()  # 類直接呼叫類方法,結果同上面的例項呼叫
>>> 
類方法,類:__main__.MyClass,val1:Value 1,無法訪問val2的值

>>> mc.val1 = 'Value changed'  # 改變例項變數val1的值

>>> mc.classmd()  # 例項呼叫類方法,注意到cls.val1的值沒變,所以,這時的cls.val1是類變數val1,而非例項變數val1
>>> 
類方法,類:__main__.MyClass,val1:Value 1,無法訪問val2的值

>>> MyClass.classmd()  # 類直接呼叫類方法,結果同上面的例項呼叫
>>> 
類方法,類:__main__.MyClass,val1:Value 1,無法訪問val2的值

>>> MyClass.val1 = 'Class Value changed'  # 改變類變數val1的值

>>> mc.classmd()  # 例項呼叫類方法,注意到cls.val1的值變了,所以,進一步證明了這時的cls.val1是類變數val1,而非例項變數val1
>>> 
類方法,類:__main__.MyClass,val1:Class Value changed,無法訪問val2的值

>>> MyClass.classmd()  # 類直接呼叫類方法,結果同上面的例項呼叫
>>> 
類方法,類:__main__.MyClass,val1:Class Value changed,無法訪問val2的值

1. 靜態方法: 無法訪問類屬性、例項屬性,相當於一個相對獨立的方法,跟類其實麼什麼關係,換個角度來講,其實就是放在一個類的作用域裡的函式而已。

2.類方法: 可以訪問類屬性,無法訪問例項屬性。

總結

1. 例項方法需要至少一個預設引數self。

2. 類方法需要至少一個預設引數cls。

3. 靜態方法不需要預設引數。

另外:方法也是屬性,所以在類的內部,我們可以通過self.abc(),cls.abc()的方式來呼叫類中的其它方法,當然要注意傳參的問題。