你真的瞭解Python中的類class?

概述

在Python的類中,有着類屬性、實例屬性,靜態方法、類方法、實例方法的區別。到底有什麼不同呢?接下來咱們就一探究竟。python


類屬性、實例屬性

來看下簡單的 Student 類的例子程序員

class Student(object):

    # 類屬性
    school = '井岡山大學'

    def __init__(self, name):

        # 實例屬性
        self.name = name
複製代碼

其中 schoolStudent 類的類屬性,name 則是實例屬性。編程

ipython 中測試一下如何訪問其屬性markdown

In [5]: stu1 = Student('hui')

In [6]: stu2 = Student('wang')

In [7]: stu3 = Student('zack')

In [8]: stu1.name, Student.school
Out[8]: ('hui', '井岡山大學')

In [9]: stu2.name, Student.school
Out[9]: ('wang', '井岡山大學')

In [10]: stu3.name, Student.school
Out[10]: ('zack', '井岡山大學')

# 看看實例對象能不能訪問類屬性,類對象能不能訪問實例屬性
In [11]: stu1.name, stu1.school
Out[11]: ('hui', '井岡山大學')

In [12]: Student.name, stu1.school
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-12-b897e001b174> in <module>
----> 1 Student.name, stu1.school

AttributeError: type object 'Student' has no attribute 'name'
複製代碼

通過測試能夠發現 實例屬性須要經過實例對象來訪問,類屬性經過類來訪問,但在測驗中 stu1.school 實例對象也能訪問類屬性,爲何呢?編程語言

其實,實例對象也是間接的經過類對象進行訪問的,在每個實例對象中都有一個 __class__ 的屬性,其指向的就是建立實例對象的類對象。stu1.__class__ 的指向就是 Student類對象。而後實例對象訪問屬性的規則是先訪問實例屬性,而後再根據實例對象的 __class__ 來訪問類屬性。若是都沒有找到則報錯。測試

In [15]: dir(stu1)
Out[15]:
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 
 ....
 
 'name',
 'school']

In [16]: stu1.__class__
Out[16]: __main__.Student

In [17]: stu1.__class__.school
Out[17]: '井岡山大學'

In [18]: id(Student)
Out[18]: 2011692023944

In [19]: id(stu1.__class__)
Out[19]: 2011692023944
複製代碼

能夠看出 Studentstu1.__class__id() 都同樣,說明其內存地址都同樣。所以實例屬性能夠經過 __class__ 訪問類屬性。ui

存儲方式以下圖spa

類對象派生實例對象


由上圖能夠看出:code

  • 類屬性在內存中只保存一份
  • 實例屬性在每一個對象中都要保存一份

仍是以上面的例子在 ipython 中對類屬性的修改進行測驗orm

In [24]: class Student(object):
    ...:
    ...:     # 類屬性
    ...:     school = '井岡山大學'
    ...:
    ...:     def __init__(self, name):
    ...:
    ...:         # 實例屬性
    ...:         self.name = name
    ...:

In [25]: stu1 = Student('hui')

In [26]: stu2 = Student('jack')

In [27]: stu1.name, stu1.school
Out[27]: ('hui', '井岡山大學')

In [28]: stu2.name, stu2.school
Out[28]: ('jack', '井岡山大學')

# 經過類對象進行修改
In [29]: Student.school = '清華大學'

In [30]: stu2.name, stu2.school
Out[30]: ('jack', '清華大學')

In [31]: stu1.name, stu1.school
Out[31]: ('hui', '清華大學')

# 經過實例對象進行修改
IIn [33]: stu1.school = '北京大學'

In [34]: stu1.name, stu1.school
Out[34]: ('hui', '北京大學')

In [35]: stu2.name, stu2.school
Out[35]: ('jack', '清華大學')

In [36]: Student.school
Out[36]: '清華大學'

In [37]: stu1.__class__.school
Out[37]: '清華大學'
    
In [39]: id(stu2.school)
Out[39]: 2011720409808

In [40]: id(Student.school)
Out[40]: 2011720409808

In [41]: id(stu1.school)
Out[41]: 2011720494992

# 經過實例對象的__class__屬性修改
IIn [42]: stu2.__class__.school = '井岡山大學'

In [43]: stu1.name, stu1.school
Out[43]: ('hui', '北京大學')

In [44]: stu2.name, stu2.school
Out[44]: ('jack', '井岡山大學')

In [45]: Student.school
Out[45]: '井岡山大學'
複製代碼

說明: 實例對象.類屬性 = xxx 並無修改到其類屬性,而是在實例對象中建立了一個與類屬性同名的實例屬性。所以修改類屬性,應該使用類對象進行修改。再外界最好不要使用 實例對象.新屬性 = xxx,動態建立實例屬性。


使用場景

究竟是用類屬性,仍是實例屬性?

若是每一個實例對象須要具備相同值的屬性,那麼就使用類屬性,用一份既可。

class Province(object):
    # 類屬性
    country = '中國'

    def __init__(self, name):
        # 實例屬性
        self.name = name
        
p1 = Province('江西省')
p2 = Province('四川省')
        
複製代碼

實例方法、靜態方法和類方法

類中方法包括:實例方法、靜態方法和類方法,三種方法在內存中都歸屬於類,區別在於調用方式不一樣

  • 實例方法:由對象調用,至少一個 self 參數;執行實例方法時,自動將調用該方法的對象賦值給 self
  • 類方法:由類調用,至少一個 cls 參數;執行類方法時,自動將調用該方法的類賦值給 cls
  • 靜態方法:由類調用,無默認參數。

class Foo(object):

    foo = 'Foo'

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

    def instance_func(self):
        print(self.name)
        print(self.foo)
        print('實例方法')

 @classmethod
    def class_func1(cls):
        print(cls.foo)
        print('類方法1')

 @classmethod
    def class_func2(cls):
        print(cls.name)
        print('類方法二')

 @staticmethod
    def static_func():
        print('靜態方法')

複製代碼

其中 @classmethod 是裝飾器,說明這是類方法,@staticmethod 則說明是靜態方法。關於裝飾器的內容這裏就不在贅述了。

ipython 中測驗一下各方法

# 實例對象調用
In [71]: f = Foo('hui')

In [72]: f.instance_func()
hui
Foo
實例方法

In [73]: f.class_func1()
Foo
類方法1

In [74]: f.class_func2()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-74-7d161e9e60ec> in <module>
----> 1 f.class_func2()

<ipython-input-60-7fc48649a96a> in class_func2(cls)
     18     @classmethod
     19     def class_func2(cls):
---> 20         print(cls.name)
     21         print('類方法二')
     22

AttributeError: type object 'Foo' has no attribute 'name'

In [75]: f.static_func()
靜態方法

# 類對象自身調用
In [76]: Foo.instance_func()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-76-883efcb56130> in <module>
----> 1 Foo.instance_func()

TypeError: instance_func() missing 1 required positional argument: 'self'

In [77]: Foo.class_func1()
Foo
類方法1

In [78]: Foo.static_func()
靜態方法
複製代碼

能夠發現實例對象三種方法均可以調用,但 cls 類對象不能訪問實例屬性。類對象不能直接調用實例方法,類、靜態方法能夠。


self與cls的區別

  • self 指的是類實例對象自己(注意:不是類自己)。
  • cls 指的是類對象自己
  • self 能夠訪問到類屬性、實例屬性,cls 只能訪問類屬性。

其中 self, cls 只是代指實例對象和類對象,所以換成其餘變量也能夠,可是約定成俗(爲了和其餘編程語言統一,減小理解難度),不要搞另類,你們會不明白的。


使用場景

須要操做類屬性的定義成類方法。

須要操做實例屬性的定義成實例方法。

既不須要操做類屬性,也不須要操做實例屬性就定義成靜態方法。


公衆號

新建文件夾X

大天然用數百億年創造出咱們現實世界,而程序員用幾百年創造出一個徹底不一樣的虛擬世界。咱們用鍵盤敲出一磚一瓦,用大腦構建一切。人們把1000視爲權威,咱們反其道行之,捍衛1024的地位。咱們不是鍵盤俠,咱們只是平凡世界中不凡的締造者 。

相關文章
相關標籤/搜索