Python Language
Des classes
Recherche…
Introduction
Python s'offre non seulement comme un langage de script populaire, mais prend également en charge le paradigme de programmation orientée objet. Les classes décrivent des données et fournissent des méthodes pour manipuler ces données, toutes regroupées sous un seul objet. De plus, les classes permettent l'abstraction en séparant les détails concrets de la mise en œuvre des représentations abstraites des données.
Les classes utilisant le code sont généralement plus faciles à lire, à comprendre et à gérer.
Héritage de base
L'héritage dans Python est basé sur des idées similaires utilisées dans d'autres langages orientés objet tels que Java, C ++, etc. Une nouvelle classe peut être dérivée d'une classe existante comme suit.
class BaseClass(object):
pass
class DerivedClass(BaseClass):
pass
La BaseClass
est la classe ( parent ) existante et la classe DerivedClass
est la nouvelle classe ( enfant ) qui hérite (ou sous - classe ) les attributs de BaseClass
. Remarque : à partir de Python 2.2, toutes les classes héritent implicitement de la classe d' object
, qui est la classe de base de tous les types intégrés.
Nous définissons une classe Rectangle
parent dans l'exemple ci-dessous, qui hérite implicitement de l' object
:
class Rectangle():
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def perimeter(self):
return 2 * (self.w + self.h)
La classe Rectangle
peut être utilisée comme classe de base pour définir une classe Square
, car un carré est un cas particulier de rectangle.
class Square(Rectangle):
def __init__(self, s):
# call parent constructor, w and h are both s
super(Square, self).__init__(s, s)
self.s = s
La classe Square
héritera automatiquement de tous les attributs de la classe Rectangle
ainsi que de la classe d'objet. super()
est utilisé pour appeler la __init__()
de la classe Rectangle
, appelant essentiellement toute méthode surchargée de la classe de base. Note : dans Python 3, super()
ne nécessite aucun argument.
Les objets de classe dérivés peuvent accéder et modifier les attributs de ses classes de base:
r.area()
# Output: 12
r.perimeter()
# Output: 14
s.area()
# Output: 4
s.perimeter()
# Output: 8
Fonctions intégrées qui fonctionnent avec l'héritage
issubclass(DerivedClass, BaseClass)
: renvoie True
si DerivedClass
est une sous-classe de BaseClass
isinstance(s, Class)
: renvoie True
si s est une instance de Class
ou de l'une des classes dérivées de Class
# subclass check
issubclass(Square, Rectangle)
# Output: True
# instantiate
r = Rectangle(3, 4)
s = Square(2)
isinstance(r, Rectangle)
# Output: True
isinstance(r, Square)
# Output: False
# A rectangle is not a square
isinstance(s, Rectangle)
# Output: True
# A square is a rectangle
isinstance(s, Square)
# Output: True
Variables de classe et d'instance
Les variables d'instance sont uniques pour chaque instance, tandis que les variables de classe sont partagées par toutes les instances.
class C:
x = 2 # class variable
def __init__(self, y):
self.y = y # instance variable
C.x
# 2
C.y
# AttributeError: type object 'C' has no attribute 'y'
c1 = C(3)
c1.x
# 2
c1.y
# 3
c2 = C(4)
c2.x
# 2
c2.y
# 4
Les variables de classe sont accessibles sur les instances de cette classe, mais l'affectation à l'attribut class créera une variable d'instance qui masque la variable de classe
c2.x = 4
c2.x
# 4
C.x
# 2
Notez que la mutation de variables de classe à partir d'instances peut entraîner des conséquences inattendues.
class D:
x = []
def __init__(self, item):
self.x.append(item) # note that this is not an assigment!
d1 = D(1)
d2 = D(2)
d1.x
# [1, 2]
d2.x
# [1, 2]
D.x
# [1, 2]
Méthodes liées, non liées et statiques
L'idée des méthodes liées et non liées a été supprimée dans Python 3 . Dans Python 3, lorsque vous déclarez une méthode dans une classe, vous utilisez un mot-clé def
, créant ainsi un objet fonction. Ceci est une fonction régulière et la classe environnante fonctionne comme son espace de noms. Dans l'exemple suivant, nous déclarons la méthode f
dans la classe A
, qui devient une fonction Af
:
class A(object):
def f(self, x):
return 2 * x
A.f
# <function A.f at ...> (in Python 3.x)
Dans Python 2, le comportement était différent: les objets fonction de la classe étaient implicitement remplacés par des objets de type instancemethod
, appelés méthodes non liées, car ils n'étaient liés à aucune instance de classe particulière. Il était possible d'accéder à la fonction sous-jacente en utilisant la propriété .__func__
.
A.f
# <unbound method A.f> (in Python 2.x)
A.f.__class__
# <type 'instancemethod'>
A.f.__func__
# <function f at ...>
Ces derniers comportements sont confirmés par inspection - les méthodes sont reconnues comme des fonctions dans Python 3, tandis que la distinction est confirmée dans Python 2.
import inspect
inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
import inspect
inspect.isfunction(A.f)
# False
inspect.ismethod(A.f)
# True
Dans les deux versions de Python, la fonction / méthode Af
peut être appelée directement, à condition que vous passiez une instance de classe A
en premier argument.
A.f(1, 7)
# Python 2: TypeError: unbound method f() must be called with
# A instance as first argument (got int instance instead)
# Python 3: 14
a = A()
A.f(a, 20)
# Python 2 & 3: 40
Maintenant, supposons a
est une instance de la classe A
, qu'est-ce que af
alors? Eh bien, intuitivement, cela devrait être la même méthode f
de la classe A
, mais elle devrait en quelque sorte "savoir" qu'elle a été appliquée à l'objet a
- en Python, cela s'appelle la méthode liée à a
.
Les détails de la débrouille sont les suivantes: l' écriture af
invoque la magie __getattribute__
méthode d' a
, qui vérifie d' abord si a
a un attribut nommé f
(il n'a pas), puis vérifie la classe A
si elle contient une méthode avec un tel nom (il le fait), et crée un nouvel objet m
de type method
qui a la référence à l' Af
origine dans m.__func__
, et une référence à l'objet a
in m.__self__
. Lorsque cet objet est appelé en tant que fonction, il fait simplement ce qui suit: m(...) => m.__func__(m.__self__, ...)
. Ainsi, cet objet est appelé méthode liée car, lorsqu'il est appelé, il sait fournir l'objet auquel il était lié en tant que premier argument. (Ces choses fonctionnent de la même manière dans Python 2 et 3).
a = A()
a.f
# <bound method A.f of <__main__.A object at ...>>
a.f(2)
# 4
# Note: the bound method object a.f is recreated *every time* you call it:
a.f is a.f # False
# As a performance optimization you can store the bound method in the object's
# __dict__, in which case the method object will remain fixed:
a.f = a.f
a.f is a.f # True
Enfin, Python a des méthodes de classe et des méthodes statiques - des types spéciaux de méthodes. Les méthodes de classe fonctionnent de la même manière que les méthodes classiques, sauf que lorsqu'elles sont appelées sur un objet, elles sont liées à la classe de l'objet plutôt qu'à l'objet. Donc m.__self__ = type(a)
. Lorsque vous appelez une telle méthode liée, elle passe la classe d' a
comme premier argument. Les méthodes statiques sont encore plus simples: elles ne lient rien du tout et renvoient simplement la fonction sous-jacente sans aucune transformation.
class D(object):
multiplier = 2
@classmethod
def f(cls, x):
return cls.multiplier * x
@staticmethod
def g(name):
print("Hello, %s" % name)
D.f
# <bound method type.f of <class '__main__.D'>>
D.f(12)
# 24
D.g
# <function D.g at ...>
D.g("world")
# Hello, world
Notez que les méthodes de classe sont liées à la classe même si elles sont accessibles sur l'instance:
d = D()
d.multiplier = 1337
(D.multiplier, d.multiplier)
# (2, 1337)
d.f
# <bound method D.f of <class '__main__.D'>>
d.f(10)
# 20
Il est à noter qu'au niveau le plus bas, les fonctions, méthodes, méthodes statiques, etc. sont en réalité des descripteurs qui invoquent les méthodes spéciales __get__
, __set
__ et éventuellement __del__
. Pour plus de détails sur les méthodes de classe et les méthodes statiques:
- Quelle est la différence entre @staticmethod et @classmethod en Python?
- Signification de @classmethod et @staticmethod pour les débutants?
Nouveau style vs classes anciennes
Les classes de nouveau style ont été introduites dans Python 2.2 pour unifier les classes et les types . Ils héritent du type d' object
niveau supérieur. Une classe de nouveau style est un type défini par l'utilisateur et très similaire aux types intégrés.
# new-style class
class New(object):
pass
# new-style instance
new = New()
new.__class__
# <class '__main__.New'>
type(new)
# <class '__main__.New'>
issubclass(New, object)
# True
Les anciennes classes n'héritent pas de l' object
. Les anciennes instances sont toujours implémentées avec un type d' instance
intégré.
# old-style class
class Old:
pass
# old-style instance
old = Old()
old.__class__
# <class __main__.Old at ...>
type(old)
# <type 'instance'>
issubclass(Old, object)
# False
En Python 3, les anciennes classes ont été supprimées.
Les classes de nouveau style dans Python 3 héritent implicitement de l' object
, il n'est donc plus nécessaire de spécifier MyClass(object)
.
class MyClass:
pass
my_inst = MyClass()
type(my_inst)
# <class '__main__.MyClass'>
my_inst.__class__
# <class '__main__.MyClass'>
issubclass(MyClass, object)
# True
Valeurs par défaut pour les variables d'instance
Si la variable contient une valeur d'un type immuable (par exemple une chaîne), il est possible d'attribuer une valeur par défaut comme celle-ci
class Rectangle(object):
def __init__(self, width, height, color='blue'):
self.width = width
self.height = height
self.color = color
def area(self):
return self.width * self.height
# Create some instances of the class
default_rectangle = Rectangle(2, 3)
print(default_rectangle.color) # blue
red_rectangle = Rectangle(2, 3, 'red')
print(red_rectangle.color) # red
Il faut faire attention lors de l'initialisation d'objets mutables tels que les listes dans le constructeur. Prenons l'exemple suivant:
class Rectangle2D(object):
def __init__(self, width, height, pos=[0,0], color='blue'):
self.width = width
self.height = height
self.pos = pos
self.color = color
r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [4, 0] r2's pos has changed as well
Ce comportement est dû au fait que dans Python, les paramètres par défaut sont liés à l'exécution de la fonction et non à la déclaration de la fonction. Pour obtenir une variable d’instance par défaut qui n’est pas partagée entre les instances, il faut utiliser une construction comme celle-ci:
class Rectangle2D(object):
def __init__(self, width, height, pos=None, color='blue'):
self.width = width
self.height = height
self.pos = pos or [0, 0] # default value is [0, 0]
self.color = color
r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [0, 0] r2's pos hasn't changed
Voir également les arguments de Mutable par défaut et la «moindre surprise» et l'argument par défaut Mutable .
Héritage multiple
Python utilise l'algorithme de linéarisation C3 pour déterminer l'ordre de résolution des attributs de classe, y compris les méthodes. Ceci est connu comme l'Ordre de Résolution de Méthode (MRO).
Voici un exemple simple:
class Foo(object):
foo = 'attr foo of Foo'
class Bar(object):
foo = 'attr foo of Bar' # we won't see this.
bar = 'attr bar of Bar'
class FooBar(Foo, Bar):
foobar = 'attr foobar of FooBar'
Maintenant, si nous instancions FooBar, si nous recherchons l'attribut foo, nous voyons que l'attribut de Foo est trouvé en premier
fb = FooBar()
et
>>> fb.foo
'attr foo of Foo'
Voici le MRO de FooBar:
>>> FooBar.mro()
[<class '__main__.FooBar'>, <class '__main__.Foo'>, <class '__main__.Bar'>, <type 'object'>]
On peut simplement dire que l’algorithme MRO de Python est
- Profondeur d'abord (par exemple
FooBar
puisFoo
) à moins que - un parent partagé (
object
) est bloqué par un enfant (Bar
) et - aucune relation circulaire autorisée.
C'est-à-dire que, par exemple, Bar ne peut pas hériter de FooBar alors que FooBar hérite de Bar.
Pour un exemple complet en Python, voir l' entrée Wikipedia .
Une autre fonctionnalité puissante dans l'héritage est super
. super peut récupérer les fonctionnalités des classes parentes.
class Foo(object):
def foo_method(self):
print "foo Method"
class Bar(object):
def bar_method(self):
print "bar Method"
class FooBar(Foo, Bar):
def foo_method(self):
super(FooBar, self).foo_method()
Héritage multiple avec la méthode init de la classe, lorsque chaque classe a sa propre méthode d'initialisation, alors nous essayons d'inférencer plusieurs fois, alors seule la méthode init est appelée de classe qui est héritée en premier.
pour exemple ci - dessous seule méthode init Foo classe se classe appelé Bar INIT pas appelé
class Foo(object):
def __init__(self):
print "foo init"
class Bar(object):
def __init__(self):
print "bar init"
class FooBar(Foo, Bar):
def __init__(self):
print "foobar init"
super(FooBar, self).__init__()
a = FooBar()
Sortie:
foobar init
foo init
Mais cela ne signifie pas que la classe Bar n'est pas héritée. L'instance de la classe FooBar finale est également une instance de la classe Bar et de la classe Foo .
print isinstance(a,FooBar)
print isinstance(a,Foo)
print isinstance(a,Bar)
Sortie:
True
True
True
Descripteurs et recherches par points
Les descripteurs sont des objets qui sont (généralement) des attributs de classes et qui ont des méthodes spéciales __get__
, __set__
ou __delete__
.
Les descripteurs de données ont l'un des __set__
ou __delete__
Ceux-ci peuvent contrôler la recherche pointillée sur une instance et sont utilisés pour implémenter des fonctions, staticmethod
, classmethod
et property
. Une recherche pointillée (par exemple, instance foo
de la classe Foo
recherchant la bar
attributs - c.-à-d. foo.bar
) utilise l'algorithme suivant:
bar
est levé dans la classe,Foo
. S'il existe et qu'il s'agit d'un descripteur de données , le descripteur de données est utilisé. C'est comme cela que laproperty
peut contrôler l'accès aux données dans une instance et que les instances ne peuvent pas remplacer cela. Si un descripteur de données n’est pas là, alorsbar
est recherché dans l'instance__dict__
. C'est pourquoi nous pouvons remplacer ou bloquer les méthodes appelées à partir d'une instance avec une recherche pointillée. Si labar
existe dans l'instance, elle est utilisée. Sinon, nous avons alorsregardez dans la classe
Foo
forbar
. S'il s'agit d'un descripteur , le protocole de descripteur est utilisé. Voici comment les fonctions (dans ce contexte, les méthodes non liées),classmethod
etstaticmethod
sont implémentées. Sinon, il retourne simplement l'objet, ou il y a unAttributeError
Méthodes de classe: initialiseurs alternatifs
Les méthodes de classe présentent des méthodes alternatives pour générer des instances de classes. Pour illustrer, regardons un exemple.
Supposons que nous ayons une classe de Person
relativement simple:
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Il pourrait être utile d'avoir un moyen de construire des instances de cette classe en spécifiant un nom complet au lieu du nom et du prénom séparément. Une façon de le faire serait de faire en sorte que last_name
soit un paramètre facultatif, et en supposant que s’il n’est pas donné, nous avons passé le nom complet dans:
class Person(object):
def __init__(self, first_name, age, last_name=None):
if last_name is None:
self.first_name, self.last_name = first_name.split(" ", 2)
else:
self.first_name = first_name
self.last_name = last_name
self.full_name = self.first_name + " " + self.last_name
self.age = age
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Cependant, il y a deux problèmes principaux avec ce bit de code:
Les paramètres
first_name
etlast_name
sont maintenant trompeuses, puisque vous pouvez entrer un nom complet pourfirst_name
. En outre, s'il y a plus de cas et / ou de paramètres ayant ce type de flexibilité, le branchement if / elif / else peut devenir rapidement ennuyeux.Pas tout à fait aussi important, mais ça vaut quand même la peine de le souligner: et si
last_name
estNone
, maisfirst_name
ne se divise pas en deux ou plus via des espaces? Nous avons encore une autre couche de validation des entrées et / ou de gestion des exceptions ...
Entrez les méthodes de classe. Plutôt que d'avoir un seul initialiseur, nous allons créer un initialiseur distinct, appelé from_full_name
, et le décorer avec le décorateur de classmethod
(intégré).
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
@classmethod
def from_full_name(cls, name, age):
if " " not in name:
raise ValueError
first_name, last_name = name.split(" ", 2)
return cls(first_name, last_name, age)
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Remarquez cls
au lieu de self
comme premier argument de from_full_name
. Les méthodes de classe sont appliquées à la classe globale, et non à une instance d'une classe donnée (ce que self
désigne généralement). Donc, si cls
est notre Person
de classe, la valeur renvoyée par la from_full_name
méthode de classe est Person(first_name, last_name, age)
, qui utilise la Person
de __init__
pour créer une instance de la Person
classe. En particulier, si nous devions créer une sous-classe Employee
of Person
, alors from_full_name
fonctionnerait également dans la classe Employee
.
Pour montrer que cela fonctionne comme prévu, créons des instances de Person
de plusieurs manières sans les branchements dans __init__
:
In [2]: bob = Person("Bob", "Bobberson", 42)
In [3]: alice = Person.from_full_name("Alice Henderson", 31)
In [4]: bob.greet()
Hello, my name is Bob Bobberson.
In [5]: alice.greet()
Hello, my name is Alice Henderson.
Autres références:
https://docs.python.org/2/library/functions.html#classmethod
https://docs.python.org/3.5/library/functions.html#classmethod
Composition de classe
La composition de classe permet des relations explicites entre les objets. Dans cet exemple, les gens vivent dans des villes appartenant à des pays. La composition permet aux personnes d'accéder au nombre de personnes vivant dans leur pays:
class Country(object):
def __init__(self):
self.cities=[]
def addCity(self,city):
self.cities.append(city)
class City(object):
def __init__(self, numPeople):
self.people = []
self.numPeople = numPeople
def addPerson(self, person):
self.people.append(person)
def join_country(self,country):
self.country = country
country.addCity(self)
for i in range(self.numPeople):
person(i).join_city(self)
class Person(object):
def __init__(self, ID):
self.ID=ID
def join_city(self, city):
self.city = city
city.addPerson(self)
def people_in_my_country(self):
x= sum([len(c.people) for c in self.city.country.cities])
return x
US=Country()
NYC=City(10).join_country(US)
SF=City(5).join_country(US)
print(US.cities[0].people[0].people_in_my_country())
# 15
Singe Patching
Dans ce cas, "patching singe" signifie l'ajout d'une nouvelle variable ou méthode à une classe après sa définition. Par exemple, disons que nous avons défini la classe A
comme
class A(object):
def __init__(self, num):
self.num = num
def __add__(self, other):
return A(self.num + other.num)
Mais maintenant, nous voulons ajouter une autre fonction plus tard dans le code. Supposons que cette fonction est la suivante.
def get_num(self):
return self.num
Mais comment ajouter cela comme méthode dans A
? C'est simple, nous plaçons essentiellement cette fonction dans A
avec une déclaration d'affectation.
A.get_num = get_num
Pourquoi ça marche? Parce que les fonctions sont des objets comme n'importe quel autre objet, et que les méthodes sont des fonctions appartenant à la classe.
La fonction get_num
doit être disponible pour tous les existants (déjà créés) ainsi que pour les nouvelles instances de A
Ces ajouts sont disponibles automatiquement sur toutes les instances de cette classe (ou de ses sous-classes). Par exemple:
foo = A(42)
A.get_num = get_num
bar = A(6);
foo.get_num() # 42
bar.get_num() # 6
Notez que, contrairement à d'autres langages, cette technique ne fonctionne pas pour certains types intégrés et n'est pas considérée comme un bon style.
Liste de tous les membres de la classe
La fonction dir()
peut être utilisée pour obtenir une liste des membres d'une classe:
dir(Class)
Par exemple:
>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Il est courant de ne chercher que des membres "non magiques". Cela peut être fait en utilisant une compréhension simple qui répertorie les membres dont le nom ne commence pas par __
:
>>> [m for m in dir(list) if not m.startswith('__')]
['append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Mises en garde:
Les classes peuvent définir une __dir__()
. Si cette méthode existe, appeler dir()
appellera __dir__()
, sinon Python essaiera de créer une liste des membres de la classe. Cela signifie que la fonction dir peut avoir des résultats inattendus. Deux citations importantes de la documentation officielle de python :
Si l'objet ne fournit pas dir (), la fonction essaie de son mieux de collecter des informations à partir de l'attribut dict de l'objet, s'il est défini, et de son objet de type. La liste résultante n'est pas nécessairement complète et peut être inexacte lorsque l'objet a un getattr () personnalisé.
Remarque: Étant donné que rép () est principalement utilisé pour une utilisation sur une invite interactive, il essaie de fournir un ensemble de noms intéressant plus qu’il ne tente de fournir un ensemble de noms rigoureusement ou systématiquement défini, et son comportement détaillé peut varier rejets. Par exemple, les attributs de métaclasse ne sont pas dans la liste de résultats lorsque l'argument est une classe.
Introduction aux cours
Une classe fonctionne comme un modèle définissant les caractéristiques de base d'un objet particulier. Voici un exemple:
class Person(object):
"""A simple class.""" # docstring
species = "Homo Sapiens" # class attribute
def __init__(self, name): # special method
"""This is the initializer. It's a special
method (see below).
"""
self.name = name # instance attribute
def __str__(self): # special method
"""This method is run when Python tries
to cast the object to a string. Return
this string when using print(), etc.
"""
return self.name
def rename(self, renamed): # regular method
"""Reassign and print the name attribute."""
self.name = renamed
print("Now my name is {}".format(self.name))
Il y a quelques points à noter lorsque vous regardez l'exemple ci-dessus.
- La classe est composée d' attributs (données) et de méthodes (fonctions).
- Les attributs et les méthodes sont simplement définis comme des variables et des fonctions normales.
- Comme indiqué dans le docstring correspondant, la
__init__()
s'appelle l' initialiseur . C'est l'équivalent du constructeur dans d'autres langages orientés objet, et c'est la méthode qui est exécutée en premier lorsque vous créez un nouvel objet ou une nouvelle instance de la classe. - Les attributs qui s'appliquent à la classe entière sont définis en premier et sont appelés attributs de classe .
- Les attributs qui s'appliquent à une instance spécifique d'une classe (un objet) sont appelés attributs d'instance . Ils sont généralement définis dans
__init__()
; ce n'est pas nécessaire, mais cela est recommandé (puisque les attributs définis en dehors de__init__()
risquent d'être consultés avant d'être définis). - Chaque méthode, incluse dans la définition de classe, passe l'objet en question comme premier paramètre. Le mot
self
est utilisé pour ce paramètre (l'utilisation deself
est en réalité par convention, car le motself
n'a aucune signification inhérente en Python, mais c'est l'une des conventions les plus respectées de Python, et vous devriez toujours la suivre). - Ceux qui sont habitués à la programmation orientée objet dans d'autres langues peuvent être surpris par certaines choses. L'une d'elles est que Python n'a pas de véritable concept d'éléments
private
, donc tout, par défaut, imite le comportement du mot clépublic
C ++ / Java. Pour plus d'informations, reportez-vous à l'exemple "Private Class Members" sur cette page. - Certaines des méthodes de la classe ont la forme suivante:
__functionname__(self, other_stuff)
. Toutes ces méthodes sont appelées "méthodes magiques" et constituent une partie importante des classes en Python. Par exemple, la surcharge de l'opérateur en Python est implémentée avec des méthodes magiques. Pour plus d'informations, consultez la documentation appropriée .
Faisons maintenant quelques exemples de notre classe Person
!
>>> # Instances
>>> kelly = Person("Kelly")
>>> joseph = Person("Joseph")
>>> john_doe = Person("John Doe")
Nous avons actuellement trois objets Person
, kelly
, joseph
et john_doe
.
Nous pouvons accéder aux attributs de la classe à partir de chaque instance à l'aide de l'opérateur point .
Notez à nouveau la différence entre les attributs de classe et d'instance:
>>> # Attributes
>>> kelly.species
'Homo Sapiens'
>>> john_doe.species
'Homo Sapiens'
>>> joseph.species
'Homo Sapiens'
>>> kelly.name
'Kelly'
>>> joseph.name
'Joseph'
Nous pouvons exécuter les méthodes de la classe en utilisant le même opérateur de points .
:
>>> # Methods
>>> john_doe.__str__()
'John Doe'
>>> print(john_doe)
'John Doe'
>>> john_doe.rename("John")
'Now my name is John'
Propriétés
Les classes Python prennent en charge les propriétés , qui ressemblent à des variables d'objet classiques, mais avec la possibilité d'attacher un comportement et une documentation personnalisés.
class MyClass(object):
def __init__(self):
self._my_string = ""
@property
def string(self):
"""A profoundly important string."""
return self._my_string
@string.setter
def string(self, new_value):
assert isinstance(new_value, str), \
"Give me a string, not a %r!" % type(new_value)
self._my_string = new_value
@string.deleter
def x(self):
self._my_string = None
L'objet de la classe MyClass
semblera avoir une propriété .string
, mais son comportement est maintenant étroitement contrôlé:
mc = MyClass()
mc.string = "String!"
print(mc.string)
del mc.string
Outre la syntaxe utile ci-dessus, la syntaxe de la propriété permet la validation ou d’autres augmentations à ajouter à ces attributs. Cela pourrait être particulièrement utile avec les API publiques - où un niveau d'aide devrait être donné à l'utilisateur.
Une autre utilisation courante des propriétés est de permettre à la classe de présenter des «attributs virtuels» - des attributs qui ne sont pas réellement stockés mais qui ne sont calculés que sur demande.
class Character(object):
def __init__(name, max_hp):
self._name = name
self._hp = max_hp
self._max_hp = max_hp
# Make hp read only by not providing a set method
@property
def hp(self):
return self._hp
# Make name read only by not providing a set method
@property
def name(self):
return self.name
def take_damage(self, damage):
self.hp -= damage
self.hp = 0 if self.hp <0 else self.hp
@property
def is_alive(self):
return self.hp != 0
@property
def is_wounded(self):
return self.hp < self.max_hp if self.hp > 0 else False
@property
def is_dead(self):
return not self.is_alive
bilbo = Character('Bilbo Baggins', 100)
bilbo.hp
# out : 100
bilbo.hp = 200
# out : AttributeError: can't set attribute
# hp attribute is read only.
bilbo.is_alive
# out : True
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : False
bilbo.take_damage( 50 )
bilbo.hp
# out : 50
bilbo.is_alive
# out : True
bilbo.is_wounded
# out : True
bilbo.is_dead
# out : False
bilbo.take_damage( 50 )
bilbo.hp
# out : 0
bilbo.is_alive
# out : False
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : True
Classe Singleton
Un singleton est un modèle qui limite l'instanciation d'une classe à une instance / objet. Pour plus d'informations sur les modèles de conception de singleton python, voir ici .
class Singleton:
def __new__(cls):
try:
it = cls.__it__
except AttributeError:
it = cls.__it__ = object.__new__(cls)
return it
def __repr__(self):
return '<{}>'.format(self.__class__.__name__.upper())
def __eq__(self, other):
return other is self
Une autre méthode consiste à décorer votre classe. En suivant l'exemple de cette réponse, créez une classe Singleton:
class Singleton:
"""
A non-thread-safe helper class to ease implementing singletons.
This should be used as a decorator -- not a metaclass -- to the
class that should be a singleton.
The decorated class can define one `__init__` function that
takes only the `self` argument. Other than that, there are
no restrictions that apply to the decorated class.
To get the singleton instance, use the `Instance` method. Trying
to use `__call__` will result in a `TypeError` being raised.
Limitations: The decorated class cannot be inherited from.
"""
def __init__(self, decorated):
self._decorated = decorated
def Instance(self):
"""
Returns the singleton instance. Upon its first call, it creates a
new instance of the decorated class and calls its `__init__` method.
On all subsequent calls, the already created instance is returned.
"""
try:
return self._instance
except AttributeError:
self._instance = self._decorated()
return self._instance
def __call__(self):
raise TypeError('Singletons must be accessed through `Instance()`.')
def __instancecheck__(self, inst):
return isinstance(inst, self._decorated)
Pour utiliser, vous pouvez utiliser la méthode Instance
@Singleton
class Single:
def __init__(self):
self.name=None
self.val=0
def getName(self):
print(self.name)
x=Single.Instance()
y=Single.Instance()
x.name='I\'m single'
x.getName() # outputs I'm single
y.getName() # outputs I'm single