This article is the 9th day article of Python Part 3 Advent Calendar 2019. In this article, I will look back on the basics of the Python class as a memorandum. This article is based on some of the following books.
This function is used when you do not want to refer to or update the variables in the class from the outside. First, define the class (User) normally.
class User:
def __init__(self, name=None):
self.name = name
user = User(name='qiita')
print('user name: ', user.name) # user name: qiita
I would like to add a property called flag
to a new class that inherits from this class. If you want to rewrite and make this property unreadable from the outside, add __
(two underscores) in front of the property.
class User2(User):
def __init__(self, name=None, flag=True):
super().__init__(name)
self.__flag = flag
user2 = User2(name='qiita')
print('user2 flag: ', user2.__flag) # AttributeError
However, it can be accessed from inside the class. If you try the following code, the value of flag
will be output.
class User2(User):
def __init__(self, name=None, flag=True):
super().__init__(name)
self.__flag = flag
print('flag: ', self.__flag) # flag: True
user2 = User2(name='qiita')
(Addition) Furthermore, as shown below, you can access by adding _class name
in front of the property. Also, I didn't know about this feature in PEP8, it seems that it should not be actively used in the metaphor of class properties, but only to avoid property name collisions.
class User2(User):
def __init__(self, name=None, flag=True):
super().__init__(name)
self.__flag = flag
user2 = User2(name='qiita')
print(user2._User2__flag) # True
Also, although it cannot be rewritten from the outside, it is possible to define a new normal property as well. (I don't think I do this much)
class User2(User):
def __init__(self, name=None, flag=True):
super().__init__(name)
self.__flag = flag
user2 = User2(name='qiita')
user2.__flag = 'changed'
print('user2 flag: ', user2.__flag) # user2 flag: changed
There is one underscore as a flexible metaphor than the two underscores I mentioned earlier. However, if you use this method without any ingenuity, it will be normally accessible from the outside. (Be careful as the developer does not want to rewrite it)
class User3(User):
def __init__(self, name=None, flag=True):
super().__init__(name)
self._flag = flag
user3 = User3(name='qiita')
print('user3 flag: ', user3._flag) # user3 flag: True
user3._flag = False
print('user3 flag: ', user3._flag) # user3 flag: False
Therefore, we will devise this property. It is possible to define flag
as a property that can be referenced from the outside by defining a property together with the decorator as shown below, but cannot be rewritten.
class User3(User):
def __init__(self, name=None, flag=True):
super().__init__(name)
self._flag = flag
@property
def flag(self):
return self._flag
user3 = User3(name='qiita')
print('user3 flag: ', user3.flag) # user3 flag: True
user3.flag = False # AttributeError: can't set attribute
However, even in the above case, if you call it with ._flag
instead of .flag
, you can rewrite it normally, so be careful. Also, by using @property name.setter
, it is possible to rewrite as well as refer to it. In this case, it is often used together with ʻif etc. as
rewritable only when certain conditions are met. In the code below, the flag
property can be rewritten only when the pswd
property matches the condition.
class User3(User):
def __init__(self, name=None, flag=True, pswd=None):
super().__init__(name)
self._flag = flag
self.pswd = pswd
@property
def flag(self):
return self._flag
@flag.setter
def flag(self, new_flag):
if self.pswd=='777':
self._flag = new_flag
else:
pass
user3 = User3(name='qiita', flag=True, pswd='222')
user3.flag = False
print('user3 flag: ', user3.flag) # user3 flag: True ->Not rewritten
In the above example, pass
is used, but exception handling may be used to cause an error. In the case of the above example, it is necessary to consider the possibility of bugs caused by the fact that the intention was rewritten but not rewritten.
If you define a special method in the class, you can operate the instance using operators. If you look it up, you'll find a lot, but this time I'd like to pick up some of them.
class Add:
def __init__(self, value):
self.value = value
# [+]Special method called when using
def __add__(self, other):
return self.value + other.value
# [-]Special method called when using
def __sub__(self, other):
return self.value + other.value + 5
x = Add(10)
print(type(x)) # <class '__main__.Add'>
print(x + Add(5)) # 15
print(x - Add(5)) # 20
The __add__
method above is a special method called by using [+] after the instance.
Other examples (excerpt)
Arithmetic operator | Method |
---|---|
* | mul |
/ | truediv |
& | and |
The specifications are almost the same as the above arithmetic operators.
class Equ:
def __init__(self, value):
self.value = value
# [==]Special method called when using
def __eq__(self, other):
return self.value == other.value
print(Equ(str(4)) == Equ(str(4))) # True
Other examples (excerpt)
Comparison operator | Method |
---|---|
!= | ne |
< | lt |
> | gt |
class Int:
def __init__(self, num):
self.num = num
def __int__(self):
return int(self.num)
x = Int('1')
print(type(x)) # <class '__main__.Int'>
print(type(int(x))) # <class 'int'>
print(int(x)) # 1
The above is a special method called when using the built-in function ʻint, which, as the name implies, converts the object itself to an int type. In addition, ʻint ('100') == 100 # True
, which is often used in Python programming, is also a function that can be realized by calling the __int __ ()
method defined in the default str object. ..
Other examples (excerpt)
effect | Method |
---|---|
To float type | float |
To string type | str |
class Dataset:
def __init__(self):
self.imgs = ['img01', 'img02', 'img03']
def __getitem__(self, idx):
return self.imgs[idx]
def __len__(self):
return len(self.imgs)
dataset = Dataset()
print(dataset[0]) # img01
print(len(dataset)) # 3
The above writing style is a pattern often used in PyTorch and so on. A container type is an object that has multiple values, such as a list or dictionary. These are usually accessed by []
and by len
to refer to the length, but the instance itself can be accessed in the same way.
A python class can define variables that can be used in common within a class even if it is not initialized by initialization. The former is called an instance variable and the latter is called a class variable. Class variables are shared when there are multiple instances created from the same class, so if you have a mutable value as a class variable, it will lead to a bug.
class Sort:
num_list = []
def num_sort(self, nums):
self.num_list += sorted(nums)
return self.num_list
num_list1 = Sort().num_sort([3, 5, 10, 2])
print(num_list1) # [2, 3, 5, 10]
num_list2 = Sort().num_sort([-1, 8, 0, -2])
print(num_list2) # [2, 3, 5, 10, -2, -1, 0, 8]
In the above example, the class variable of num_list1
is also shared with num_list2
, which is strange. If you define it as an instance variable with __init__
, it will be initialized every time you declare an instance, so this will not happen.
class Sort:
def __init__(self):
self.num_list = []
def num_sort(self, nums):
self.num_list += sorted(nums)
return self.num_list
num_list1 = Sort().num_sort([3, 5, 10, 2])
print(num_list1) # [2, 3, 5, 10]
num_list2 = Sort().num_sort([-1, 8, 0, -2])
print(num_list2) # [-2, -1, 0, 8]
At the end, I tried to summarize the functions that I personally find useful. Python also allows you to inherit built-in types and create your own original objects. In the example below, a new object type is created by inheriting the dictionary type.
class Dict2(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__dict__ = self
dic = Dict2(i=1, j=2, k=3)
print(dic.i, dic.j, dic.k)
In the above example, unlike the normal dictionary type, you can refer to the value with .
. It may be natural for those who are accustomed to other programming languages. With a normal Python dictionary type, you have to access dic ['i']
every time.
duck typing You can use the same methods in different classes, and you can switch between the same operations on different objects. This is called duck typing.
def animal_cry(animal):
animal.cry()
class Duck:
def cry(self):
print('ga-ga-')
class Turkey:
def cry(self):
print('bo-bo-')
duck = Duck()
turkey = Turkey()
for ani in [duck, turkey]:
animal_cry(ani)
# ga-ga-
# bo-bo-
The above example applies the same cry
method to instances created from different classes duck and bird. If you take advantage of this, you may be able to write more object-oriented and concise code.
class Bird:
def __init__(self, voice):
self.voice = voice
if type(voice) != str:
raise AttributeError('The argument voice must be str type.')
def cry(self):
print(self.voice)
class Duck(Bird):
def __init__(self, voice='ga-ga-'):
super().__init__(voice)
class Turkey(Bird):
def __init__(self, voice='bo-bo-'):
super().__init__(voice)
class DuckCry:
def bird_cry(self, bird):
if bird.voice != 'ga-ga-':
print("It's don't duck.")
else:
bird.cry()
duck = Duck()
turkey = Turkey()
duck_cry = DuckCry().bird_cry(duck) # ga-ga-
duck_cry = DuckCry().bird_cry(turkey) # It's don't duck.
Although it is said to be concise code, the example is not good and I can not express its convenience, but I am making a class that inherits one class, creates various classes, and executes common methods for each. Here is an example.
Regarding the number of articles by language of Advent Calender 2019, it seems that Go is all filled up to that 3 compared to Python 3 which is not filled yet. (As of 12/7) I feel that Python is more populous, but do Python users use other platforms than Qiita to disseminate information? (It's true that there are many machine learning areas such as Hatena blogs.) I hope that Python will continue to excite, and I will turn the baton to the next person.
Recommended Posts