[Python] Est-ce que l'objet nul mais non vide est Vrai ou Chute?

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

Discutez de Truthy et Farsy

Qu'est-ce que Truthy / 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.

Position sur la vérité / la fausseté

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".

Modèle A: Un seul vrai et un tombé

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.

Modèle B: toutes les valeurs sont soit Vrai soit Chute

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 ~~

Motif C: il existe plusieurs Vérité / Faux, mais certaines valeurs ne sont ni

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.

Avantages / inconvénients du langage d'authenticité étendue

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

  1. L'expression ʻa = 5 renvoie la valeur 5 (quelle que soit la valeur de ʻa)
  2. «5» est la vérité
  3. Si le contenu du bloc est exécuté!

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

Truthy / Falsy en Python

Qu'est-ce qui décide Farsy

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.

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,

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.

En cas de conflit

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.

En Python3

À 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

[Python] Est-ce que l'objet nul mais non vide est Vrai ou Chute?
Python est douloureux. Mais utilisez
[Python] Qui est exécuté en premier, variable de classe ou __init__?
[Python] Qu'est-ce que @? (À propos des décorateurs)
Quel est le meilleur, PyPy ou Python?
À quoi sert le trait de soulignement Python (_)?
Python ne génère pas d'erreurs ou de sortie simplement parce que le retrait est mal aligné
[Python] Détermine si un point de coordonnée est à l'intérieur ou à l'extérieur du polygone
Prenez la somme logique de List en Python (fonction zip)
Où est écrit le processus d'instanciation python?
[Python3] Réécrire l'objet code de la fonction
Qu'est-ce que "mahjong" dans la bibliothèque Python? ??
[python] [meta] Le type de python est-il un type?
Qu'est-ce qui est le plus rapide, le mélange Python ou l'échantillon?