Python基礎-類別變數和實例變數

巴扎黑
發布: 2017-06-26 09:12:11
原創
1492 人瀏覽過

Python基礎-類別變數與實例變數

寫在前面

如非特別說明,下文皆基於Python3

大綱:

Python基礎-類別變數和實例變數

1. 類別變數與實例變數

在Python Tutorial中對於類別變數和實例變數是這樣描述的:

Generally speaking, instance variables are for data unique to each instance and class variables are for attributes and methods shared by all instances of the class:

通常來說,實例變數是對每個實例都獨有的數據,而類別變數是該類別所有實例共享的屬性和方法。

其實我比較願意用類別屬性和實例屬性來稱呼它們,但是變數這個字已經成為程式語言的習慣稱謂。一個正常的範例是:

class Dog:

    kind = 'canine'         # class variable shared by all instancesdef __init__(self, name):self.name = name    # instance variable unique to each instance
登入後複製

類別Dog中,類別屬性kind為所有實例所共用;實例屬性name 為每個Dog的實例獨有。

2. 類別物件與實例物件

2.1 類別物件

Python中一切皆物件;類別定義完成後,會在目前作用域中定義一個以類別名為名字,指向類別物件的名字。如

class Dog:pass
登入後複製

會在目前作用域定義名字Dog,指向類別物件Dog

類別物件支援的操作
總的來說,類別物件僅支援兩個操作:

  1. 實例化;使用instance_name = class_name()的方式實例化,實例化作業會建立該類別的實例。

  2. 屬性參考;使用class_name.attr_name的方式來引用類別屬性。

2.2 實例物件

實例物件是類別物件實例化的產物,實例物件只支援一個操作:

  1. 屬性參考;與類別物件屬性所引用的方式相同,使用instance_name.attr_name的方式。

依照嚴格的物件導向思想,所有屬性都應該是實例的,類別屬性不應該存在。那麼在Python中,由於類別屬性綁定就不應該存在,類別定義中就只剩下函數定義了。

在Python tutorial關於類別定義也這麼說:

In practice, the statements inside a class definition will usually be function definitions, but other statements are allowed, and sometimes usefulful.在

實作中,類別定義中的語句通常是函數定義,但是其他語句也是允許的,有時也是有用的。

這裡說的其他語句,就是指類別屬性的綁定語句。

3. 屬性綁定

在定義類別時,通常我們說的定義屬性,其實是分成兩個面向的:

  1. 類別屬性綁定

  2. 實例屬性綁定

#用綁定這個字更確切;不管是類別物件還是實例對象,屬性都是依托對象而存在的。

我們說的屬性綁定,首先需要一個可變對象,才能執行綁定操作,使用

objname.attr = attr_value
登入後複製

的方式,為對象objname綁定屬性attr

這分成兩種情況:

  1. 若屬性attr已經存在,綁定操作會將屬性名稱指向新的物件;

  2. 若不存在,則為該物件新增新的屬性,後面就可以引用新增屬性。

3.1 類別屬性綁定

Python作為動態語言,類別物件和實例物件都可以在執行時間綁定任意屬性。因此,類別屬性的綁定發生在兩個地方:

  1. 類別定義時;

  2. 運行時任意階段。

下面這個範例說明了類別屬性綁定發生的時期:

class Dog:

    kind = 'canine'Dog.country = 'China'print(Dog.kind, ' - ', Dog.country) # output: canine  -  Chinadel Dog.kindprint(Dog.kind, ' - ', Dog.country) # AttributeError: type object 'Dog' has no attribute 'kind'
登入後複製

在類別定義中,類別屬性的綁定並沒有使用objname.attr = attr_value的方式,這是一個特例,其實是等同於後面使用類別名稱綁定屬性的方式。
因為是動態語言,所以可以在執行時增加屬性,刪除屬性。

3.2 實例屬性綁定

與類別屬性綁定相同,實例屬性綁定也發生在兩個地方:

  1. 類別定義時;

  2. 運行時任意階段。

範例:

class Dog:def __init__(self, name, age):self.name = nameself.age = age

dog = Dog('Lily', 3)
dog.fur_color = 'red'print('%s is %s years old, it has %s fur' % (dog.name, dog.age, dog.fur_color))# Output: Lily is 3 years old, it has red fur
登入後複製

#Python類別實例有兩個特殊之處:

  1. __init__在實例化時執行

  2. #Python實例呼叫方法時,會將實例物件作為第一個參數傳遞

因此,__init__方法中的self就是實例物件本身,這裡是dog,語句

self.name = nameself.age = age
登入後複製

以及后面的语句

dog.fur_color = 'red'
登入後複製

为实例dog增加三个属性name, age, fur_color

4. 属性引用

属性的引用与直接访问名字不同,不涉及到作用域。

4.1 类属性引用

类属性的引用,肯定是需要类对象的,属性分为两种:

  1. 数据属性

  2. 函数属性

数据属性引用很简单,示例:

class Dog:

    kind = 'canine'Dog.country = 'China'print(Dog.kind, ' - ', Dog.country) # output: canine  -  China
登入後複製

通常很少有引用类函数属性的需求,示例:

class Dog:

    kind = 'canine'def tell_kind():print(Dog.kind)
        
Dog.tell_kind() # Output: canine
登入後複製

函数tell_kind在引用kind需要使用Dog.kind而不是直接使用kind,涉及到作用域,这一点在我的另一篇文章中有介绍:Python进阶 - 命名空间与作用域

4.2 实例属性引用

使用实例对象引用属性稍微复杂一些,因为实例对象可引用类属性以及实例属性。但是实例对象引用属性时遵循以下规则:

  1. 总是先到实例对象中查找属性,再到类属性中查找属性;

  2. 属性绑定语句总是为实例对象创建新属性,属性存在时,更新属性指向的对象。

4.2.1 数据属性引用

示例1:

class Dog:

    kind = 'canine'country = 'China'def __init__(self, name, age, country):self.name = nameself.age = ageself.country = country

dog = Dog('Lily', 3, 'Britain')print(dog.name, dog.age, dog.kind, dog.country)# output: Lily 3 canine Britain
登入後複製

类对象Dog与实例对象dog均有属性country,按照规则,dog.country会引用到实例对象的属性;但实例对象dog没有属性kind,按照规则会引用类对象的属性。

示例2:

class Dog:

    kind = 'canine'country = 'China'def __init__(self, name, age, country):self.name = nameself.age = ageself.country = country

dog = Dog('Lily', 3, 'Britain')print(dog.name, dog.age, dog.kind, dog.country) # Lily 3 canine Britainprint(dog.__dict__) # {'name': 'Lily', 'age': 3, 'country': 'Britain'}dog.kind = 'feline'print(dog.name, dog.age, dog.kind, dog.country) # Lily 3 feline Britainprint(dog.__dict__) 
print(Dog.kind) # canine 没有改变类属性的指向# {'name': 'Lily', 'age': 3, 'country': 'Britain', 'kind': 'feline'}
登入後複製

使用属性绑定语句dog.kind = 'feline',按照规则,为实例对象dog增加了属性kind,后面使用dog.kind引用到实例对象的属性。

这里不要以为会改变类属性Dog.kind的指向,实则是为实例对象新增属性,可以使用查看__dict__的方式证明这一点。

示例3,可变类属性引用:

class Dog:
    
    tricks = []def __init__(self, name):self.name = namedef add_trick(self, trick):self.tricks.append(trick)

d = Dog('Fido')
e = Dog('Buddy')
d.add_trick('roll over')
e.add_trick('play dead')print(d.tricks) # ['roll over', 'play dead']
登入後複製

语句self.tricks.append(trick)并不是属性绑定语句,因此还是在类属性上修改可变对象。

4.2.2 方法属性引用

与数据成员不同,类函数属性在实例对象中会变成方法属性。

先看一个示例:

class MethodTest:def inner_test(self):print('in class')def outer_test():print('out of class')

mt = MethodTest()
mt.outer_test = outer_testprint(type(MethodTest.inner_test))  # <class &#39;function&#39;>print(type(mt.inner_test))          #<class &#39;method&#39;>print(type(mt.outer_test))          #<class &#39;function&#39;>
登入後複製

可以看到,类函数属性在实例对象中变成了方法属性,但是并不是实例对象中所有的函数都是方法。

Python tutorial中这样介绍方法对象:

When an instance attribute is referenced that isn’t a data attribute, its class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.

引用非数据属性的实例属性时,会搜索它对应的类。如果名字是一个有效的函数对象,Python会将实例对象连同函数对象打包到一个抽象的对象中并且依据这个对象创建方法对象:这就是被调用的方法对象。当使用参数列表调用方法对象时,会使用实例对象以及原有参数列表构建新的参数列表,并且使用新的参数列表调用函数对象。

那么,实例对象只有在引用方法属性时,才会将自身作为第一个参数传递;调用实例对象的普通函数,则不会。
所以可以使用如下方式直接调用方法与函数:

mt.inner_test()
mt.outer_test()
登入後複製

除了方法与函数的区别,其引用与数据属性都是一样的

5. 最佳实践

虽然Python作为动态语言,支持在运行时绑定属性,但是从面向对象的角度来看,还是在定义类的时候将属性确定下来。

以上是Python基礎-類別變數和實例變數的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板