Quelle est la relation entre le Python, modèle de données et fonctions internes?
- Les objets internes et les opérateurs utilisent le modèle de données sous-jacente des méthodes ou des attributs.
- Les objets internes et les opérateurs ont plus un comportement élégant et sont en général plus compatible.
- Les méthodes spéciales de datamodel sont sémantiquement non-public des interfaces.
- Les objets internes et les opérateurs de langage sont spécifiquement destinés à être l'interface utilisateur pour le comportement mis en œuvre par des méthodes spéciales.
Ainsi, vous devez vous préférez utiliser les fonctions internes et les opérateurs de l'emplacement possible sur les méthodes et les attributs du modèle de données.
La sémantique interne de l'Api sont plus susceptibles de changer, que les interfaces publiques. Alors que Python n'est pas réellement envisager quoi que ce soit "privé" et expose les internes, qui ne veut pas dire que c'est une bonne idée de l'abus que l'accès. Cette façon de faire comporte les risques suivants:
- Vous pouvez trouver que vous avez plus de modifications importantes lors de la mise à niveau de votre exécutable Python ou de passer à d'autres implémentations de Python (comme PyPy, IronPython, ou Jython, ou certains autres imprévus de la mise en œuvre.)
- Vos collègues vont probablement penser mal de vos compétences linguistiques et conscience, et le considérer comme un code-odeur, vous et le reste de votre code pour un examen plus approfondi.
- Les fonctions internes sont faciles à intercepter comportement pour. En utilisant des méthodes spéciales directement les limites de la puissance de votre Python pour l'introspection et le débogage.
En profondeur
Les fonctions internes et les opérateurs d'invoquer des méthodes spéciales et l'utilisation des attributs particuliers dans le Python datamodel. Ils sont les lisible et maintenable placage qui cache la structure interne des objets. En général, les utilisateurs doivent utiliser les objets internes et les opérateurs dans la langue, par opposition à l'appel des méthodes spéciales ou en utilisant les attributs spéciaux directement.
Les fonctions internes et les opérateurs peuvent aussi avoir de repli ou plus élégant de comportement que les plus primitifs datamodel des méthodes spéciales. Par exemple:
-
next(obj, default)
permet de fournir une valeur par défaut au lieu de lever StopIteration
quand un itérateur s'épuise, alors qu' obj.__next__()
ne le sont pas.
-
str(obj)
fallsback d' obj.__repr__()
lorsque obj.__str__()
n'est pas disponible - considérant que l'appelant obj.__str__()
directement poserait un attribut d'erreur.
-
obj != other
fallsback d' not obj == other
en Python 3 lorsque n __ne__
- appelant obj.__ne__(other)
ne serait pas profiter de cette.
(Builtin, les fonctions peuvent être facilement éclipsé, si nécessaire ou souhaitable, sur un module de portée mondiale ou l' builtins
module, pour personnaliser le comportement.)
Cartographie les objets internes et les opérateurs pour le modèle de données
Voici une cartographie, avec des notes, des fonctions internes et des opérateurs pour les méthodes et les attributs qu'ils utilisent ou de retour de noter que la règle habituelle est que la fonction builtin généralement des cartes à une méthode spéciale du même nom, mais ce n'est pas assez conséquente pour justifier de donner à cette carte ci-dessous:
builtins/ special methods/
operators -> datamodel NOTES (fb == fallback)
repr(obj) obj.__repr__() provides fb behavior for str
str(obj) obj.__str__() fb to __repr__ if no __str__
bytes(obj) obj.__bytes__() Python 3 only
unicode(obj) obj.__unicode__() Python 2 only
format(obj) obj.__format__() format spec optional.
hash(obj) obj.__hash__()
bool(obj) obj.__bool__() Python 3, fb to __len__
bool(obj) obj.__nonzero__() Python 2, fb to __len__
dir(obj) obj.__dir__()
vars(obj) obj.__dict__ does not include __slots__
type(obj) obj.__class__ type actually bypasses __class__ -
overriding __class__ will not affect type
help(obj) obj.__doc__ help uses more than just __doc__
len(obj) obj.__len__() provides fb behavior for bool
iter(obj) obj.__iter__() fb to __getitem__ w/ indexes from 0 on
next(obj) obj.__next__() Python 3
next(obj) obj.next() Python 2
reversed(obj) obj.__reversed__() fb to __len__ and __getitem__
other in obj obj.__contains__(other) fb to __iter__ then __getitem__
obj == other obj.__eq__(other)
obj != other obj.__ne__(other) fb to not obj.__eq__(other) in Python 3
obj < other obj.__lt__(other) get >, >=, <= with @functools.total_ordering
complex(obj) obj.__complex__()
int(obj) obj.__int__()
float(obj) obj.__float__()
round(obj) obj.__round__()
abs(obj) obj.__abs__()
L' operator
module length_hint
qui a une solution de secours mis en œuvre par une méthode particulière, si __len__
n'est pas mis en œuvre:
length_hint(obj) obj.__length_hint__()
En Pointillé Recherches
En pointillé recherches contextuelles. Sans méthode particulière de mise en œuvre, de regarder d'abord dans la hiérarchie de classes de descripteurs de données (comme les propriétés et les slots), puis dans l'instance __dict__
(pour les variables d'instance), puis dans la hiérarchie de classe pour les non-descripteurs de données (comme des méthodes). Des méthodes spéciales pour mettre en œuvre les comportements suivants:
obj.attr obj.__getattr__('attr') provides fb if dotted lookup fails
obj.attr obj.__getattribute__('attr') preempts dotted lookup
obj.attr = _ obj.__setattr__('attr', _) preempts dotted lookup
del obj.attr obj.__delattr__('attr') preempts dotted lookup
Les descripteurs de
Les descripteurs sont un peu avancé - n'hésitez pas à passer ces entrées et revenir plus tard - rappeler le descripteur de l'instance se trouve dans la hiérarchie de classe (comme des méthodes, des logements et des propriétés). Un descripteur de données met en œuvre, soit __set__
ou __delete__
:
obj.attr descriptor.__get__(obj, type(obj))
obj.attr = val descriptor.__set__(obj, val)
del obj.attr descriptor.__delete__(obj)
Lorsque la classe est instanciée (défini) le descripteur de méthode __set_name__
est appelé si un descripteur pour informer le descripteur de son nom d'attribut. (Ce qui est nouveau en Python 3.6.) cls
est le même que type(obj)
- dessus, et 'attr'
représente le nom de l'attribut:
class cls:
@descriptor_type
def attr(self): pass # -> descriptor.__set_name__(cls, 'attr')
Les éléments (indice de notation)
L'indice de notation est également contextuelle:
obj[name] -> obj.__getitem__(name)
obj[name] = item -> obj.__setitem__(name, item)
del obj[name] -> obj.__delitem__(name)
Un cas particulier pour les sous-classes d' dict
, __missing__
est appelé si __getitem__
ne trouve pas la clé:
obj[name] -> obj.__missing__(name)
Les opérateurs
Il existe également des méthodes pour +, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |
opérateurs, par exemple:
obj + other -> obj.__add__(other), fallback to other.__radd__(obj)
obj | other -> obj.__or__(other), fallback to other.__ror__(obj)
et les opérateurs augmentée d'affectation, +=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=
, par exemple:
obj += other -> obj.__iadd__(other)
obj |= other -> obj.__ior__(other)
et les opérations unaires:
+obj -> obj.__pos__()
-obj -> obj.__neg__()
~obj -> obj.__invert__()
Contexte Les Gestionnaires
Un gestionnaire de contexte définit __enter__
, qui est appelé à entrer dans le bloc de code (sa valeur de retour, généralement de soi, est un alias avec as
), et __exit__
, qui est garanti pour être appelé en quittant le bloc de code, à l'exception de l'information.
with obj as cm: -> cm = obj.__enter__()
raise Exception('message')
-> obj.__exit__(Exception, Exception('message'), traceback_object)
Si __exit__
obtient une exception, puis renvoie une valeur fausse, il sur-relance au départ la méthode.
Si aucune exception, __exit__
reçoit None
pour ces trois arguments au lieu de cela, et la valeur de retour est dénuée de sens:
with obj: -> obj.__enter__()
pass
-> obj.__exit__(None, None, None)
Certains Métaclasse Méthodes Spéciales
De même, les classes peuvent avoir des méthodes spéciales (à partir de leur metaclasses) qui prennent en charge les classes de base abstraites:
isinstance(obj, cls) -> cls.__instancecheck__(obj)
issubclass(sub, cls) -> cls.__subclasscheck__(sub)
Une importante vente à emporter est que, bien que les fonctions internes telles que next
et bool
ne changent pas entre Python 2 et 3, sous-jacents à la mise en œuvre noms sont en train de changer.
Ainsi, en utilisant les objets internes offre aussi plus de la compatibilité ascendante.
Quand suis-je censé utiliser les noms spéciaux?
En Python, les noms qui commencent par des caractères de soulignement sont sémantiquement non-public des noms pour les utilisateurs. Le trait de soulignement est le créateur de la façon de dire, "hands-off, ne les touchez pas."
Ce n'est pas seulement culturel, mais il est aussi en Python du traitement de l'API. Quand un paquet __init__.py
utilise import *
de fournir une API à partir d'un sous-paquetage, si le sous-paquetage ne fournit pas un __all__
,, elle exclut les noms qui commencent par des caractères de soulignement. Le sous-paquetage de l' __name__
seraient également exclus.
IDE auto-complétion des outils sont mélangés dans leur prise en compte des noms qui commencent par un caractère de soulignement non-public. Cependant, j'apprécie beaucoup de ne pas voir l' __init__
, __new__
, __repr__
, __str__
, __eq__
, etc. (ni créés par l'utilisateur non-public des interfaces) quand je tape le nom d'un objet et une durée.
J'ai donc affirmer:
La spéciale "dsous" méthodes ne sont pas une partie de l'interface publique. Éviter de les utiliser directement.
Donc, quand les utiliser?
Les principaux cas d'utilisation est lors de la mise en œuvre de votre propre objet personnalisé ou sous-classe d'une builtin objet.
Essayez de ne les utiliser que lorsque c'est absolument nécessaire. Voici quelques exemples:
Utiliser l' __name__
attribut spécial sur les fonctions ou classes
Lorsque nous ajoutons une fonction, il est généralement une fonction wrapper en retour, qui cache des informations utiles sur la fonction. Nous devons utiliser la @wraps(fn)
décorateur pour nous assurer de ne pas perdre cette information, mais si nous avons besoin du nom de la fonction, nous devons utiliser l' __name__
attribut directement:
from functools import wraps
def decorate(fn):
@wraps(fn)
def decorated(*args, **kwargs):
print('calling fn,', fn.__name__) # exception to the rule
return fn(*args, **kwargs)
return decorated
De même, je ne le suivant quand j'ai besoin du nom de la classe de l'objet dans une méthode (utilisée, par exemple, un __repr__
):
def get_class_name(self):
return type(self).__name__
# ^ # ^- must use __name__, no builtin e.g. name()
# use type, not .__class__
En utilisant les attributs spéciaux pour écrire des classes personnalisées ou sous-classé les builtins
Lorsque l'on veut définir un comportement, nous devons utiliser les données, les noms de modèles.
Cela a du sens, puisque nous sommes les réalisateurs, ces attributs ne sont pas privés de nous.
class Foo(object):
# required to here to implement == for instances:
def __eq__(self, other):
# but we still use == for the values:
return self.value == other.value
# required to here to implement != for instances:
def __ne__(self, other): # docs recommend for Python 2.
# use the higher level of abstraction here:
return not self == other
Cependant, même dans ce cas, nous n'utilisons pas d' self.value.__eq__(other.value)
ou not self.__eq__(other)
(voir ma réponse ici pour preuve que celui-ci peut entraîner un comportement inattendu.) Au lieu de cela, nous devrions utiliser le niveau d'abstraction supérieur.
Un autre point à qui nous devons utiliser les noms de méthode, c'est quand nous sommes dans un enfant de mise en œuvre, et souhaitez déléguer à la société mère. Par exemple:
class NoisyFoo(Foo):
def __eq__(self, other):
print('checking for equality')
# required here to call the parent's method
return super(NoisyFoo, self).__eq__(other)
Conclusion
Des méthodes spéciales permettent aux utilisateurs de mettre en œuvre l'interface pour objet des éléments internes.
Utiliser les fonctions internes et les opérateurs partout où vous le pouvez. Utiliser uniquement des méthodes spéciales où il n'y a pas de documentation API publique.