I looked back on Python classes again

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.

-Minna no Python 4th Edition

Encapsulation

pattern 1

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

Pattern 2

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.

Special method

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.

Arithmetic operators

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

Comparison operator

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

Special methods that define type conversions

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

Special method used in container type

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.

Class variables / instance variables

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]

Built-in inheritance

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.

Finally

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

I looked back on Python classes again
I ran python on windows
Looking back on Python 2020 around me
[Python] Looking back on what I taught programming beginners from functions
Python on Windows
twitter on python3
I started python
python on mac
Python on Windbg
Create a Python execution environment on IBM i
I can't ask you again (?) Python Knowledge Series -Decorator-
I tried Python on Mac for the first time.
I tried to implement Minesweeper on terminal with python
I tried python on heroku for the first time
I made a Python3 environment on Ubuntu with direnv.
I want to inherit to the back with python dataclass
I want to AWS Lambda with Python on Mac!
I tried input interpolation on UE4 Python VS Code
Python conda on cygwin
Install python on WSL
PyOpenGL setup on Python 3
Install Python on Pidora.
Install Scrapy on python3
Looking back on ABC155
Python classes are slow
Install Python on Mac
Install Python 3 on Mac
Install Python3.4 on CentOS 6.6
Installing pandas on python2.6
I implemented Python Logging
python basic on windows ②
Install python on windows
Relearn Python (Algorithm I)
Python basic course (13 classes)
Install Python 2.7.3 on CentOS 5.4
build Python on Ubuntu
Install Python 3.3 on Ubuntu 12.04
Install Python 3.4 on Mac
I tried Python> decorator
Why I chose Python
I compared Python more-itertools 2.5 → 2.6
Install Python 3.6 on Docker
maya Python I want to fix the baked animation again.
I want to automatically attend online classes with Python + Selenium!
I tried changing the python script from 2.7.11 to 3.6.0 on windows10