特に断りのない限り、以下はPython3に基づいています
概要:
一般的に言えば、インスタンス変数は次のようになります。クラス変数は各インスタンスに固有のデータであり、クラス変数はクラスのすべてのインスタンスで共有されるプロパティとメソッドです。
実際、私はそれらをクラス属性とインスタンス属性と呼びたいのですが、
変数という言葉はプログラミング言語の慣例的な名前になっています。通常の例は次のとおりです。
class Dog: kind = 'canine' # class variable shared by all instancesdef __init__(self, name):self.name = name # instance variable unique to each instance
Dog
では、クラス属性 kind
はすべてのインスタンスによって共有されます。 Dog
の各インスタンスに固有のものです。 2. クラス オブジェクトとインスタンス オブジェクトDog
中,类属性kind
为所有实例所共享;实例属性name
为每个Dog
的实例独有。
Python
中一切皆对象;类定义完成后,会在当前作用域中定义一个以类名为名字,指向类对象的名字。如
class Dog:pass
会在当前作用域定义名字Dog
,指向类对象Dog
。
类对象支持的操作:
总的来说,类对象仅支持两个操作:
实例化;使用instance_name = class_name()
的方式实例化,实例化操作创建该类的实例。
属性引用;使用class_name.attr_name
的方式引用类属性。
实例对象是类对象实例化的产物,实例对象仅支持一个操作:
属性引用;与类对象属性引用的方式相同,使用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 useful.
实践中,类定义中的语句通常是函数定义,但是其他语句也是允许的,有时也是有用的。
这里说的其他语句,就是指类属性的绑定语句。
在定义类时,通常我们说的定义属性,其实是分为两个方面的:
类属性绑定
实例属性绑定
用绑定这个词更加确切;不管是类对象还是实例对象,属性都是依托对象而存在的。
我们说的属性绑定,首先需要一个可变对象,才能执行绑定操作,使用
objname.attr = attr_value
的方式,为对象objname
绑定属性attr
。
这分两种情况:
若属性attr
已经存在,绑定操作会将属性名指向新的对象;
若不存在,则为该对象添加新的属性,后面就可以引用新增属性。
Python
作为动态语言,类对象和实例对象都可以在运行时绑定任意属性。因此,类属性的绑定发生在两个地方:
类定义时;
运行时任意阶段。
下面这个例子说明了类属性绑定发生的时期:
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
的方式,这是一个特例,其实是等同于后面使用类名绑定属性的方式。
因为是动态语言,所以可以在运行时增加属性,删除属性。
与类属性绑定相同,实例属性绑定也发生在两个地方:
类定义时;
运行时任意阶段。
示例:
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
类实例有两个特殊之处:
__init__
在实例化时执行
Python
实例调用方法时,会将实例对象作为第一个参数传递
因此,__init__
方法中的self
就是实例对象本身,这里是dog
Python
内のすべてのものはオブジェクトです。クラス定義が完了すると、クラス名が定義されます。現在のスコープは名前であり、クラス オブジェクトの名前を指します。たとえば、self.name = nameself.age = age
Dog
を定義し、クラスオブジェクトDog
を指します。 🎜🎜🎜クラス オブジェクトによってサポートされる操作🎜:Instance_name = class_name; ()
がインスタンス化され、インスタンス化操作によってクラスのインスタンスが作成されます。 🎜class_name.attr_name
を使用します。 🎜instance_name.attr_name
を使用します。 🎜Python
では、クラス属性バインディングが存在しないはずなので、クラス定義には関数定義のみが残ります。 🎜🎜Python チュートリアルではクラス定義について次のように述べています:🎜🎜🎜実際には、クラス定義内のステートメントは通常関数定義になりますが、他のステートメントも許可されており、場合によっては便利です。🎜🎜🎜実際には、クラス定義内のステートメントは関数定義になります。クラス定義は通常は関数定義ですが、他のステートメントも許可され、場合によっては便利なステートメントが関数定義になりますが、他のステートメントも許可され、場合によっては便利です。 🎜🎜ここで言及されている他のステートメントは、クラス属性のバインディング ステートメントを指します。 🎜🎜3. 属性バインディング🎜🎜クラスを定義するとき、私たちが通常属性の定義と呼ぶものは、実際には 2 つの側面に分けられます: 🎜dog.fur_color = 'red'
attr</ をオブジェクト <code>objname
コードにバインドします。 >。 🎜🎜2 つの状況があります: 🎜attr
が既に存在する場合、バインディング操作は属性名が新しいオブジェクトを指すようになります。 ; 🎜Python
動的言語として、クラス オブジェクトとインスタンス オブジェクトの両方が実行時に任意の属性をバインドできます。したがって、クラス属性のバインディングは 2 つの場所で行われます: 🎜class Dog: kind = 'canine'Dog.country = 'China'print(Dog.kind, ' - ', Dog.country) # output: canine - China
objname.attr = attr_value</ / code> メソッド、これは特殊なケースであり、実際には後でクラス名を使用して属性をバインドするメソッドと同等です。 <br/>動的言語であるため、実行時に属性を追加および削除できます。 🎜<h5>3.2 インスタンス属性バインディング</h5>🎜 クラス属性バインディングと同様に、インスタンス属性バインディングも 2 つの場所で発生します。 🎜<ol class=" list-paddingleft-2"><li>🎜 クラス 定義されている場合。 </li><li>🎜実行時の任意のステージ。 🎜</li></ol>🎜例: 🎜🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="sourceCode python">class Dog:
kind = &#39;canine&#39;def tell_kind():print(Dog.kind)
Dog.tell_kind() # Output: canine</pre><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div>🎜🎜<code>Python
クラス インスタンスには 2 つの特別な機能があります: 🎜Python
インスタンスがメソッドを呼び出すと、インスタンス オブジェクトが最初のパラメータとして渡されます🎜__init__
メソッドの self
はインスタンス オブジェクト自体であり、ここでは dog
というステートメント 🎜🎜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.fur_color = 'red'
为实例dog
增加三个属性name
, age
, fur_color
。
属性的引用与直接访问名字不同,不涉及到作用域。
类属性的引用,肯定是需要类对象的,属性分为两种:
数据属性
函数属性
数据属性引用很简单,示例:
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进阶 - 命名空间与作用域
使用实例对象引用属性稍微复杂一些,因为实例对象可引用类属性以及实例属性。但是实例对象引用属性时遵循以下规则:
总是先到实例对象中查找属性,再到类属性中查找属性;
属性绑定语句总是为实例对象创建新属性,属性存在时,更新属性指向的对象。
示例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)
并不是属性绑定语句,因此还是在类属性上修改可变对象。
与数据成员不同,类函数属性在实例对象中会变成方法属性。
先看一个示例:
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 'function'>print(type(mt.inner_test)) #<class 'method'>print(type(mt.outer_test)) #<class 'function'>
可以看到,类函数属性在实例对象中变成了方法属性,但是并不是实例对象中所有的函数都是方法。
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()
除了方法与函数的区别,其引用与数据属性都是一样的
虽然Python
作为动态语言,支持在运行时绑定属性,但是从面向对象的角度来看,还是在定义类的时候将属性确定下来。
以上がPython の基本クラス変数とインスタンス変数の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。