J'étais curieux de connaître Truthy et Fallsy en Python, c'est donc un article pour discuter de Truthy et Fallsy tout en enquêtant. Si vous n'êtes intéressé que par le titre, veuillez sauter [ici](# python% E3% 81% AB% E3% 81% 8A% E3% 81% 91% E3% 82% 8Btruthy - falsy).
Les termes Truthy et Fallsy apparaissent en JavaScript, mais le concept lui-même apparaît dans de nombreuses langues, nous allons donc l'utiliser en général ici.
Truthy est un acronyme qui indique une valeur considérée comme vraie lorsqu'elle est évaluée logiquement. Falsy est un acronyme qui indique une valeur considérée comme fausse lorsqu'elle est évaluée logiquement.
Selon la personne, seules les valeurs qui sont traitées comme vrai / faux lors d'une évaluation logique, à l'exclusion de la "valeur représentative", peuvent être appelées Truthy / Falsy, mais ici, toutes Truthy selon la définition en JavaScript. Et Farsy.
Personnellement, je pense que la position sur le langage Truthy / Falsy peut être divisée en trois modèles. Dans cet article, le motif A sera appelé "authenticité pure", et le motif B et le motif C seront appelés "authenticité étendue".
Ce type de langage a un type dédié aux valeurs de vérité, et limite les valeurs possibles à 2 types. Alternativement, il peut avoir un "objet vrai seulement" ou un "objet faux seulement" comme classe singleton. Tenter d'évaluer logiquement des valeurs autres que celles-ci entraînera une erreur.
Java est un exemple typique. Cependant, dans le cas de Java, il existe des valeurs de vérité primitives et des valeurs de vérité encapsulées, donc on peut dire que ce n'est pas exactement le cas.
Toute valeur peut être évaluée logiquement dans ce type de langage. Dans certains cas, il peut même ne pas avoir de type dédié aux valeurs de vérité. Cependant, l'existence d'une "valeur représentative" est requise comme valeur de retour pour les opérations de comparaison et les refus logiques.
Un exemple typique serait JavaScript. ~~ Vous dites donc que Java et JavaScript sont différents ~~
Personnellement, je n'ai pas une bonne impression de l'un ou de l'autre. Cependant, si l '"exception" est "nulle", il peut s'agir d'une conception valide dans un sens (décrit plus loin).
Le langage C est cela par conception. C'est parce que le langage C utilise des types entiers pour les opérations logiques, etc., et considère que tout sauf «0» est Vérité. Au contraire, seul le type entier peut être évalué logiquement.
Les langues à authenticité étendue présentent plusieurs avantages.
La première est que l'expression conditionnelle peut être simplifiée.
Par exemple, pour éviter la division zéro, vous pouvez écrire un code comme celui-ci:
if (b !== 0) {
console.log(a / b)
} else {
console.log('Il ne cassera pas à zéro.')
}
Cependant, «0» est Farsy, donc
if (b) {
console.log(a / b)
} else {
console.log('Il ne cassera pas à zéro.')
}
Vous pouvez voir que cela suffit.
L'autre est que certaines opérations logiques peuvent être généralisées.
Par exemple, dans la logique classique, la somme de la logique est une opération qui "retourne false lorsque les deux côtés gauche et droit sont faux, et renvoie vrai sinon", mais c'est "lorsque le côté gauche est vrai, le côté gauche est faux et le côté gauche est faux". Vous pouvez étendre la table de vérité existante sans la modifier en interprétant "lorsque le côté droit est renvoyé".
Vous pouvez l'utiliser pour simplifier la logique. Par exemple, la logique de la saisie de la chaîne par défaut en l'absence d'entrée (chaîne vide) est la suivante lorsque vous essayez de l'écrire directement.
if (!name) {
name = 'Les invités';
}
/*Ou utilisez l'opérateur ternaire*/
name = name ? 'Les invités' : name
Cependant, en tirant parti du fait que la chaîne vide est Farsy, nous pouvons écrire ce qui suit en utilisant la somme logique généralisée.
name = name || 'Les invités'
En effet, si nom
est Vérité (c'est-à-dire s'il ne s'agit pas d'une chaîne vide), le côté gauche est renvoyé tel quel, et s'il est faux (c'est-à-dire s'il s'agit d'une chaîne de caractères vide), le côté droit est renvoyé.
Cependant, ceux-ci présentent également des inconvénients.
Par exemple, dans le premier cas, il n'y a aucune garantie que le nombre contenu dans «b» soit réellement une valeur numérique. Si une valeur Truthy non numérique est entrée, elle contournera l'expression conditionnelle et générera une erreur d'exécution.
Ces pièges sont importants dans les langages typés dynamiquement, mais même avec des langages typés statiquement, il existe de nombreux langages dans lesquels seul Null peut être attribué de manière exceptionnelle (ce que l'on appelle Null n'est pas sûr), et si Null peut également être évalué logiquement, un comportement inattendu se produira. Cela peut arriver.
Je pense plutôt que les gens qui utilisent des langages à typage dynamique sont prêts à gérer eux-mêmes l'intégrité de type (et sont déjà habitués à exécuter des erreurs de type si souvent), donc je préfère un langage à typage statique laissé à la compilation. Cela peut être aussi dangereux que la personne qui l'utilise.
Cette spécification est également devenue le seul parent qui a créé la tristement célèbre notation Yoda. L'autre parent est la «spécification que l'opérateur d'affectation renvoie une valeur». résultat,
if (a == 5) {
/* ... */
}
J'avais l'intention d'écrire
if (a = 5) {
/* ... */
}
Même si c'est
renvoie la valeur
5 (quelle que soit la valeur de ʻa
)Et a créé un foyer de bugs. Pour éviter cela
if (5 == a) {
/* ... */
}
C'est pourquoi une étrange coutume d'écrire est née.
Cela reste dans les règles de codage héritées de votre organisation, et même dans les situations où vous n'en avez pas besoin (c'est-à-dire un langage qui ne renvoie pas de valeur pour un langage d'authenticité pure ou un opérateur d'affectation, ou un compilateur qui vous avertit que vous effectuez une affectation dans une expression conditionnelle if. Cela crée une situation où vous êtes forcé (même si vous l'utilisez). Les coutumes qui sont nées par nécessité finiront par être oubliées et deviendront des «manières» dénuées de sens qui ne sont que des formes… comme c'est souvent le cas dans le monde réel.
Pause de conversation tranquille. De cette façon, la position de Truthy / Falsy présente des avantages (efficacité) et des inconvénients (pièges) dos à dos, donc lorsque vous utilisez pour la première fois une langue avec laquelle vous entrez en contact dans votre travail, il s'agit soit d'une langue d'authenticité pure, soit d'une langue d'authenticité étendue. Il est important d'en être conscient. Plus tard, quand il s'agit de "Je ne savais pas que ...", c'est douloureux (histoire d'expérience).
Eh bien, enfin le titre. De là, nous parlerons de Python2 pendant un moment. Dans le cas de 3, je le toucherai à la fin.
Python appartient au langage d'authenticité étendue. Toutes les valeurs non-Falsy sont True. Parmi les valeurs générales, les valeurs suivantes sont False.
False
0
0.0
None
Au fait, le type bool en Python est en fait une sous-classe du type int, et "True" et "False" sont en fait équivalents à "1" et "0", respectivement. Basé sur ceci,
None
Il semble que cela puisse être généralisé, non?
En plus de la valeur spéciale «Aucun», Python définit des méthodes spéciales qui indiquent «zéro ou pas» et «longueur de collection». En d'autres termes, en les implémentant, vous pouvez contrôler si votre propre objet est Vrai ou Faux.
Essayons-le tout de suite. Le premier est lorsque ni l'un ni l'autre n'est mis en œuvre.
class MyObjA:
def __init__(self):
pass
my_obj = MyObjA()
print('Truthy' if my_obj else 'Falsy')
Truthy
Vous pouvez voir qu'il est traité comme vrai parce qu'il n'y a aucun élément qui est faux.
Ensuite, disons "zéro en termes numériques". Implémentez la méthode __nonzero__
.
class MyObjB:
def __init__(self, nz):
self.nz = nz
def __nonzero__(self):
return self.nz
my_obj = MyObjB(True)
print('Truthy' if my_obj else 'Falsy')
my_obj = MyObjB(False)
print('Truthy' if my_obj else 'Falsy')
Truthy
Falsy
Comme vous pouvez le voir, si __nonzero__
renvoie False
(pour être exact, il retourne une valeur de type int), vous pouvez voir que l'objet lui-même est traité comme False.
Ensuite, essayons "Collection vide". Implémentez la méthode __len__
.
class MyObjC:
def __init__(self, ln):
self.ln = ln
def __len__(self):
return self.ln
my_obj = MyObjC(10)
print('Truthy' if my_obj else 'Falsy')
my_obj = MyObjC(0)
print('Truthy' if my_obj else 'Falsy')
Truthy
Falsy
Comme vous pouvez le voir, si __len__
renvoie 0
(pour être exact, il renvoie une valeur de type int), vous pouvez voir que l'objet lui-même est traité comme Farsy.
Mais que se passe-t-il si ces conditions sont incompatibles les unes avec les autres? C'est ce que signifie le titre.
Quand je l'essaye réellement ...
class MyObjD:
def __init__(self, nz, ln):
self.nz = nz
self.ln = ln
def __nonzero__(self):
return self.nz
def __len__(self):
return self.ln
my_obj = MyObjD(True, 10)
print('Truthy' if my_obj else "Falsy")
my_obj = MyObjD(False, 0)
print('Truthy' if my_obj else "Falsy")
my_obj = MyObjD(True, 0)
print('Truthy' if my_obj else "Falsy")
my_obj = MyObjD(False, 10)
print('Truthy' if my_obj else "Falsy")
Truthy
Falsy
Truthy
Falsy
Comme vous pouvez le voir, "zéro ou pas" est prioritaire quelle que soit la longueur. La raison de ceci peut être lue dans le message d'erreur lorsque «len» renvoie une valeur non valide.
class MyObjC:
def __init__(self, ln):
self.ln = ln
def __len__(self):
return self.ln
my_obj = MyObjC(0.0)
print('Truthy' if my_obj else 'Falsy')
Traceback (most recent call last):
File "Main.py", line 10, in <module>
print('Truthy' if my_obj else 'Falsy')
TypeError: __nonzero__ should return an int
«« Nonzero »devrait renvoyer le type int», était en colère. En d'autres termes, on peut en déduire que l'implémentation par défaut __nonzero__
appelle __len__
. Allons vérifier. __nonzero__
est appelé par la fonction intégrée bool
.
class MyObjE:
def __init__(self, ln):
self.ln = ln
def __len__(self):
print('__len__ called!')
return self.ln
my_obj = MyObjE(0)
b = bool(my_obj)
print(b)
print(type(b))
__len__ called!
False
<type 'bool'>
Vous devinez! L'implémentation par défaut __nonzero__
appelant __len__
signifie que ce qui semblait être une vérification de __len__
dans MyObjC s'appelait en fait __nonzero__
... c'est-à-dire les deux Même si vous l'implémentez, vous ne regardez que «nonzero». Par conséquent, "un objet qui est nul mais non vide" devient False.
À propos, la différence entre le nom de la méthode spéciale __nonzero__
et le nom de la fonction intégrée bool
a été corrigée dans Python3 et est devenue __bool__
. Par conséquent, cela devient comme ça.
class MyObjD:
def __init__(self, nz, ln):
self.nz = nz
self.ln = ln
def __nonzero__(self):
return self.nz
def __len__(self):
return self.ln
my_obj = MyObjD(True, 10)
print('Truthy' if my_obj else "Falsy")
my_obj = MyObjD(False, 0)
print('Truthy' if my_obj else "Falsy")
my_obj = MyObjD(True, 0)
print('Truthy' if my_obj else "Falsy")
my_obj = MyObjD(False, 10)
print('Truthy' if my_obj else "Falsy")
Truthy
Falsy
Falsy
Truthy
class MyObjF:
def __init__(self, nz, ln):
self.nz = nz
self.ln = ln
def __bool__(self):
return self.nz
def __len__(self):
return self.ln
my_obj = MyObjF(True, 10)
print('Truthy' if my_obj else "Falsy")
my_obj = MyObjF(False, 0)
print('Truthy' if my_obj else "Falsy")
my_obj = MyObjF(True, 0)
print('Truthy' if my_obj else "Falsy")
my_obj = MyObjF(False, 10)
print('Truthy' if my_obj else "Falsy")
Truthy
Falsy
Truthy
Falsy
Et, comme son nom l'indique, «bool» n'autorise que les valeurs de retour de type bool.
class MyObjF:
def __init__(self, nz, ln):
self.nz = nz
self.ln = ln
def __bool__(self):
return self.nz
def __len__(self):
return self.ln
my_obj = MyObjF(1, 10)
print('Truthy' if my_obj else "Falsy")
Traceback (most recent call last):
File "Main.py", line 14, in <module>
print('Truthy' if my_obj else "Falsy")
TypeError: __bool__ should return bool, returned int
__bool__
n'autorise plus que le type bool, mais qu'en est-il de la spécification que l'implémentation par défaut appelle __len__
?
class MyObjE:
def __init__(self, ln):
self.ln = ln
def __len__(self):
print('__len__ called!')
return self.ln
my_obj = MyObjE(0)
b = bool(my_obj)
print(b)
print(type(b))
__len__ called!
False
<class 'bool'>
Il ne semble y avoir aucun changement dans la spécification que l'implémentation par défaut __bool__
appelle __len__
. Il semble qu'il n'y ait pas de problème car la valeur de retour de __len__
est convertie en type booléen depuis le début.
Recommended Posts