Part 1 はこちら。
1.Getting Started
Pythonを使ってOOPすなわちObject-Oriented Programmingをやってみます。
Part 1は、けっこう前に書いたのですが、今回はその続きです。
2.References
Youtubeとして、
Python OOP Tutorial 2: Class Variables by Corey Schafer
また、Documentationとして、
https://docs.python.org/3/tutorial/classes.html
を挙げておきます。
3.A Class and instances
まずは、Employee class とそのinstancesです。
class Employee:
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.email = first + '.' + last + '@company.com'
self.pay = pay
def fullname(self):
return '{} {}'.format(self.first, self.last)
emp_1 = Employee('Mark', 'Twain', 50000)
emp_2 = Employee('Test', 'User', 6000)
4.method追加
classに、methodを追加してみます。
def apply_raise(self):
self.pay = int(self.pay * 1.04)
これは、payというattributeの値を書き換えるものです。
codeの一番下に、次のものも追加します。
print(emp_1.pay)
emp_1.apply_raise()
print(emp_1.pay)
1つめのprint()は、現在のpayの表示、2つめのprint()は、payが書き換えられた後のpayの表示です。
これをrunしてみます。
2つめの表示では、4%上がっています。
5.class variableの導入
1.04の箇所をclass variableで書き換えてみましょう。
class Employee:
raise_amount = 1.04
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.email = first + '.' + last + '@company.com'
self.pay = pay
def fullname(self):
return '{} {}'.format(self.first, self.last)
def apply_raise(self):
self.pay = int(self.pay * raise_amount)
emp_1 = Employee('Mark', 'Twain', 50000)
emp_2 = Employee('Test', 'User', 6000)
print(emp_1.pay)
emp_1.apply_raise()
print(emp_1.pay)
これをそのままrunするとNameErrrorが出ます。
class functionの中で使うときは、self.raise_amountとしなければなりません。
self.pay = int(self.pay * self.raise_amount)
に書き換えて、runしてみました。今度はちゃんと動きました。この場合、instanceからrasie_amountにaccessしていることになります。
さらに、classそのものからraise_amountにaccessさせることもできます。
self.pay = int(self.pay * Employee.raise_amount)
6.class variableへのaccess
(1) 次のcodeをrunしてみます。
class Employee:
raise_amount = 1.04
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.email = first + '.' + last + '@company.com'
self.pay = pay
def apply_raise(self):
self.pay = int(self.pay * self.raise_amount)
emp_1 = Employee('Mark', 'Twain', 50000)
emp_2 = Employee('Test', 'User', 6000)
print(Employee.raise_amount) # attribute
print(emp_1.raise_amount)
print(emp_2.raise_amount)
classそれ自体のraise_amountは、1.04です。emp_1のraise_amountは、emp_1自体には保存されていなくて、その場合、classにて参照、すなわちEmployee.raise_amountをすることになっています。したがって、2行目のprint()文も1.04が出ます。3行目もemp_2がclassを参照して1.04が出ます。
(2) emp_1とemp_2が自分の中ではなくて、Employeeを参照していることを確認するために、次の3行を付け加えます。
print(Employee.__dict__) # print what Employee has
print(emp_1.__dict__) # print what emp_1 has
print(emp_2.__dict__) # print what emp_2 has
これは、class及びinstancesがその中に何を持っているかを、dictionary形式で表示するものです。
runしてみます。
このうち、最初の3行の1.04は、先ほどと同じく、Employee.raise_amount、emp_1.raise_amount、emp_2.raise_amountをprintしたものです。
4~7行目の{...}は、Employeeのdictionaryの中身で、その最初の方に、
'raise_amount': 1.04
とあり、これが参照されて、先ほどの1.04がprintされていました。
8行目の{...}は、emp_1の中身で、そこには、raise_amountはありません。
9行目の{...}は、emp_2の中身で、そこにも、raise_amountはありません。
このように、instanceにおいて、特定のattributeが求められたにもかかわらず、自分の中にそれがない場合は、classを参照することになり、この場合には、Employeeの中身を参照します。その結果、
print(emp_1.raise_amount)
print(emp_2.raise_amount)
も、1.04を出力することができたのでした。
(3) さらに、print(Employee.raise_amount) # attribute のすぐ上に、
Employee.raise_amount = 1.1
を追加してみます。Codeは次のようになりました。
Employee.raise_amount = 1.1
print(Employee.raise_amount) # attribute
print(emp_1.raise_amount)
print(emp_2.raise_amount)
print(Employee.__dict__) # print what Employee has
print(emp_1.__dict__) # print what emp_1 has
print(emp_2.__dict__) # print what emp_2 has
これをrunすると、Employee.raise_amountの中身だけ変化します。そして、emp_1.raise_amountとemp_2.raise_amountは、自分の中にraise_amountを持たないので、Employeeを参照します。結果的に、いずれも、1.1が出力されます。
(4) 次に、Employee.raise_amount = 1.1としていた箇所を、
emp_1.raise_amount = 1.5としてみます。
emp_1.raise_amount = 1.5 # add an attribute to emp_1
print(Employee.raise_amount) # attribute
print(emp_1.raise_amount)
print(emp_2.raise_amount)
print(Employee.__dict__) # print what Employee has
print(emp_1.__dict__) # print what emp_1 has
print(emp_2.__dict__) # print what emp_2 has
出力は、どうなるでしょうか。
付け加えた行は、emp_1に、新たにattributeを追加するものです。
したがって、emp_1.__dict__の中に、'raise_amount' = 1.1が追加されるはずです。また、emp_1は、自分の中に、raise_amountがあるから、それを参照して、1.5と出力します。
一方、Employeeの中のraise_amountは、1.04のままです。したがって、Employeeは、1.04を出力します。また、emp_2は、raise_amountをもっていません。したがって、Employeeを参照して、これも、1.04を出力します。
下に、runした結果を示します。
予想通り、出力値は、1.04, 1.5, 1.04となっています。また、Employeeのdictionaryでは、raise_amountは1.04のままで、emp_1のdictionaryの中に、新たにraise_amountというattributeが作られ、1.5とされています。emp_2のdictionaryは変化なしです。
つまり、class variableは、結局は、attributeと同じということになります。attributeとの違いは、init methodの中にこれがないので、instanceのattributeには、class variableは含まれない点です。
もう1度、上記(2)の出力を見てみましょう。
Employeeのattributeの中には、raise_amountがありますが、emp_1とemp_2のattributeの中には、raise_amountがありません。instantsの中に、class variableのattributeが作られないのが、class attributeの特徴だということができます。
7.class variableの追加
num_of_empsというclass variableをclass Employeeに追加して、それを使った式をinit methodの中に追加します。
class Employee:
num_of_emps = 0
raise_amount = 1.04
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.email = first + '.' + last + '@company.com'
self.pay = pay
Employee.num_of_emps += 1 # increment by 1
def apply_raise(self):
self.pay = int(self.pay * self.raise_amount)
emp_1 = Employee('Mark', 'Twain', 50000)
emp_2 = Employee('Test', 'User', 6000)
print(Employee.num_of_emps)
print(emp_1.num_of_emps)
print(emp_2.num_of_emps)
print(Employee.__dict__) # print what Employee has
print(emp_1.__dict__) # print what emp_1 has
print(emp_2.__dict__) # print what emp_2 has
数式として、Employee.num_of_emps +=1の代わりに、self.num_of_emps += 1を選ぶことも考えられないことはないのですが、self.とすると、それぞれのintsanceの中でのみincrementすることになり、num_of_emps=1しかありえないことになります。一方、Employee.とすれば、各instanceを作るごとにincrementしていくことになり、num_of_employeeには常に、instancesの数が入ることになります。これは、Employeeに存在し、各instanceはこの値を持たないのですが、自分の中にないattributeはclassであるEmployeeを参照することになっているので、結局は、各instanceは、num_of_employeeの値にaccessすることができます。
したがって、上のcodeの出力値は、いずれも2となります。
8.Class Variables と Instance Variables
ChatGPTにも聞いておきます。
What is the difference between class variables and instance variables in Python?
Class Variables vs. Instance Variables in Python
In Python, class variables and instance variables are both attributes, but they have key differences in scope, behavior, and storage.
1. Class Variables (Shared by All Instances)
- Defined at the class level and shared among all instances of the class.
- Same value for all instances unless explicitly modified at the instance level.
-
Stored in the class's namespace (
ClassName.__dict__).
Example of a Class Variable
class Car:
wheels = 4 # Class variable (shared by all instances)
def __init__(self, brand):
self.brand = brand # Instance variable (unique per instance)
car1 = Car("Toyota")
car2 = Car("Honda")
print(car1.wheels) # 4
print(car2.wheels) # 4
# Changing class variable affects all instances
Car.wheels = 6
print(car1.wheels) # 6
print(car2.wheels) # 6
2. Instance Variables (Unique per Instance)
-
Defined inside the constructor (
__init__) and belong to individual instances. - Each instance has its own separate copy of the variable.
-
Stored in the instance's namespace (
instance.__dict__).
Example of an Instance Variable
class Car:
def __init__(self, brand):
self.brand = brand # Instance variable (unique per instance)
self.wheels = 4 # Instance variable (default value)
car1 = Car("Toyota")
car2 = Car("Honda")
car1.wheels = 6 # Modifies only car1's wheels
print(car1.wheels) # 6 (changed only for car1)
print(car2.wheels) # 4 (remains unchanged)
Key Differences Summary
| Feature | Class Variable | Instance Variable |
|---|---|---|
| Defined |
Inside the class (outside __init__)
|
Inside __init__
|
| Belongs to | The class itself | A specific instance |
| Shared? | Yes, shared by all instances | No, unique per instance |
| Stored in |
ClassName.__dict__
|
instance.__dict__
|
| Modified by |
ClassName.variable_name
|
self.variable_name
|
When to Use Which?
-
Use class variables for properties that should
be shared among all instances (e.g., a constant like
wheels = 4for all cars). -
Use instance variables for properties that should
be specific to each object (e.g.,
brandfor a car).
Would you like a more advanced example? 😊
class variables と instance variablesとの共通点は、どちらもattributesとなる点です。
また、違いは、class variablesは、init methodの外で定義されるのに対し、instance variablesはinit methodの中で定義される点です。
次は、Part 3です。
