J'ai repensé à la classe Python

Cet article est l'article du 9ème jour du Calendrier de l'Avent Python Partie 3 2019. Dans cet article, j'aimerais revenir sur les bases de la classe Python sous forme de mémorandum. Cet article est basé sur certains des livres suivants.

Encapsulation

modèle 1

Cette fonction est utilisée lorsque vous ne souhaitez pas faire référence ou mettre à jour les variables de la classe de l'extérieur. Tout d'abord, définissez la classe (User) normalement.

class User:
    def __init__(self, name=None):
        self.name = name

user = User(name='qiita')
print('user name: ', user.name)   # user name:  qiita

Je voudrais ajouter une propriété appelée «flag» à une nouvelle classe qui hérite de cette classe. Si vous voulez réécrire et rendre cette propriété illisible de l'extérieur, ajoutez __ (deux traits de soulignement) devant la propriété.

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

Cependant, il est accessible depuis l'intérieur de la classe. Si vous essayez le code suivant, la valeur de flag sera affichée.

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')

(Ajout) De plus, comme indiqué ci-dessous, vous pouvez y accéder en ajoutant _class name devant la propriété. De plus, comme je ne le savais pas, PEP8 semble dire que cette fonctionnalité ne devrait pas être utilisée activement dans la métaphore des propriétés de classe, mais devrait être utilisée uniquement pour éviter les conflits de nom de propriété.

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

De plus, bien qu'il ne puisse pas être réécrit de l'extérieur, il est également possible de définir une nouvelle propriété normale. (Je ne pense pas que je fasse autant)

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

Motif 2

Il y a un trait de soulignement comme métaphore flexible que les deux traits de soulignement ci-dessus. Cependant, si vous utilisez cette méthode sans aucune ingéniosité, elle sera normalement accessible de l'extérieur. (Attention car le développeur ne souhaite pas le réécrire)

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

Par conséquent, nous concevrons cette propriété. Il est possible de définir «flag» comme une propriété qui peut être référencée de l'extérieur en définissant une propriété avec un décorateur comme indiqué ci-dessous, mais ne peut pas être réécrite.

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

Cependant, même dans le cas ci-dessus, si vous l'appelez avec ._flag au lieu de .flag, vous pouvez le réécrire normalement, alors soyez prudent. De plus, en utilisant @ property name.setter, il est possible de réécrire et de s'y référer. Dans ce cas, il est souvent utilisé avec "si" etc. comme "réinscriptible uniquement lorsque certaines conditions sont remplies. Dans le code ci-dessous, la propriété flag ne peut être réécrite que lorsque la propriété pswd correspond aux conditions.

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  ->Non réécrit

Dans l'exemple ci-dessus, «pass» est utilisé, mais la gestion des exceptions peut être utilisée pour provoquer une erreur. Dans le cas de l'exemple ci-dessus, il est nécessaire de considérer la possibilité de bugs causés par le fait que l'intention a été réécrite mais pas réécrite.

Méthode spéciale

Si vous définissez une méthode spéciale dans la classe, vous pouvez utiliser l'instance à l'aide d'opérateurs. Si vous recherchez, vous en trouverez beaucoup, mais cette fois, j'aimerais en ramasser quelques-uns.

Édition d'opérateur arithmétique

class Add:
    def __init__(self, value):
        self.value = value
    
    #  [+]Méthode spéciale appelée lors de l'utilisation
    def __add__(self, other):
        return self.value + other.value
    
    #  [-]Méthode spéciale appelée lors de l'utilisation
    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

La méthode __add__ ci-dessus est une méthode spéciale appelée en utilisant [+] après l'instance.

Autres exemples (extrait)

Opérateur arithmétique Méthode
* mul
/ truediv
& and

Opérateur de comparaison

Les spécifications sont presque les mêmes que les opérateurs arithmétiques ci-dessus.

class Equ:
    def __init__(self, value):
        self.value = value
    
    #  [==]Méthode spéciale appelée lors de l'utilisation
    def __eq__(self, other):
        return self.value == other.value

print(Equ(str(4)) == Equ(str(4)))   # True

Autres exemples (extrait)

Opérateur de comparaison Méthode
!= ne
< lt
> gt

Méthode spéciale qui définit la conversion de type

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

Ce qui précède est une méthode spéciale appelée lors de l'utilisation de la fonction intégrée ʻint, qui, comme son nom l'indique, convertit l'objet lui-même en type int. De plus, ʻint ('100') == 100 # True, qui est souvent utilisé dans la programmation Python, est aussi une fonction qui peut être réalisée en appelant la méthode __int __ () définie dans l'objet str par défaut. ..

Autres exemples (extrait)

effet Méthode
Pour flotter le type float
Au type de chaîne str

Méthode spéciale utilisée dans le type de conteneur

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

Le style d'écriture ci-dessus est un modèle souvent utilisé dans PyTorch et ainsi de suite. Un type de conteneur est un objet avec plusieurs valeurs, comme une liste ou un dictionnaire. Ceux-ci sont généralement accessibles par «[]» et par «len» pour faire référence à la longueur, mais l'instance elle-même peut être accédée de la même manière.

Variables de classe / variables d'instance

Une classe python peut définir des variables qui peuvent être utilisées en commun au sein de la classe même si elle n'est pas initialisée par l'initialisation. La première est appelée variable d'instance et la seconde est appelée variable de classe. Les variables de classe sont partagées lorsqu'il y a plusieurs instances créées à partir de la même classe, ce qui peut entraîner des bogues, en particulier si vous avez des valeurs mutables comme variables de classe.

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]

Dans l'exemple ci-dessus, la variable de classe pour num_list1 est également partagée avec num_list2, ce qui est étrange. Si vous la définissez comme une variable d'instance avec __init__, elle sera initialisée à chaque fois que vous déclarerez une instance, donc cela ne se produira pas.

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]

Héritage intégré

À la fin, j'ai essayé de résumer les fonctions que je trouve personnellement utiles. En Python, il est également possible d'hériter du type intégré et de créer votre propre objet d'origine. Dans l'exemple ci-dessous, un nouveau type d'objet est créé en héritant du type de dictionnaire.

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)

Dans l'exemple ci-dessus, contrairement au type de dictionnaire normal, vous pouvez faire référence à la valeur avec .. Cela peut être naturel pour ceux qui sont habitués à d'autres langages de programmation. Avec un type de dictionnaire Python normal, vous devez accéder à dic ['i'] à chaque fois.

duck typing Vous pouvez utiliser la même méthode dans différentes classes et basculer entre les mêmes opérations sur différents objets. C'est ce qu'on appelle le typage du canard.

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-

L'exemple ci-dessus applique la même méthode cry aux instances créées à partir de différentes classes canard et bird. Si vous en tirez parti, vous pourrez peut-être écrire du code plus concis et orienté objet.

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.

Bien que l'on dise qu'il s'agit d'un code concis, l'exemple n'est pas bon et je ne peux pas exprimer sa commodité, mais je crée une classe qui hérite d'une classe, crée différentes classes et exécute des méthodes communes pour chacune. Voici un exemple.

finalement

En ce qui concerne le nombre d'articles par langue de Advent Calender 2019, il semble que Go soit tout rempli à ce 3 par rapport à Python 3 qui n'est pas encore rempli. (À partir du 12/7) Je pense que Python est plus populaire en termes de population, mais les utilisateurs de Python utilisent-ils d'autres plates-formes que Qiita pour diffuser des informations? (Il est vrai qu'il existe de nombreux domaines d'apprentissage automatique tels que les blogs Hatena.) J'espère que Python continuera à exciter, et je passerai le relais à la personne suivante.

Recommended Posts

J'ai repensé à la classe Python
J'ai exécuté python sur Windows
[Python] Rétrospective de ce que j'ai enseigné aux débutants en programmation à partir des fonctions
Python sur Windows
twitter avec python3
J'ai commencé Python
python sur mac
Python sur Windbg
Créer un environnement d'exécution Python sur IBM i
Je ne peux plus vous demander (?) Python Knowledge Series -Decorator-
J'ai essayé Python sur Mac pour la première fois.
J'ai essayé d'implémenter Mine Sweeper sur un terminal avec python
J'ai essayé python pour la première fois avec heroku
J'ai créé un environnement Python3 sur Ubuntu avec direnv.
Je veux hériter de l'arrière avec la classe de données python
Je veux AWS Lambda avec Python sur Mac!
J'ai essayé l'interpolation d'entrée pour UE4 Python VS Code
Conda Python avec Cygwin
Installer python sur WSL
Configuration de PyOpenGL sur Python 3
Installez Python sur Pidora.
Installez Scrapy sur python3
Retour sur ABC155
Les classes Python sont lentes
Installez Python sur Mac
Installer Python 3 dans un environnement Mac
Installez Python3.4 sur CentOS 6.6
Installer des pandas sur python2.6
python basic ② sous windows
Installez python sur Windows
Réapprendre Python (algorithme I)
Cours de base Python (13 cours)
Installez Python 2.7.3 sur CentOS 5.4
construire Python sur Ubuntu
Installez Python 3.3 sur Ubuntu 12.04
Installez Python 3.4 sur Mac
J'ai essayé Python> décorateur
Pourquoi j'ai choisi Python
J'ai comparé Python more-itertools 2.5 → 2.6
Installez Python 3.6 sur Docker
maya Python Je veux réparer à nouveau l'animation cuite.
Je veux assister automatiquement à des cours en ligne avec Python + Selenium!
J'ai essayé de changer le script python de 2.7.11 à 3.6.0 sur Windows10