web-dev-qa-db-fra.com

En python, existe-t-il une sorte de mappage pour renvoyer la "valeur fausse" d'un type?

Je cherche une sorte de fonction de mappage f() qui fait quelque chose de similaire à ceci:

f(str) = ''
f(complex) = 0j
f(list) = []

Cela signifie qu'il retourne un objet de type qui est évalué à False lorsqu'il est converti en bool.

Une telle fonction existe-t-elle?

40
MattS

Non, il n'y a pas de telle cartographie. Tous les types d'objets n'ont pas de valeur fausse et d'autres en ont plus d'un. Étant donné que la valeur de vérité d'une classe peut être personnalisée avec __bool__ method , une classe pourrait théoriquement avoir un nombre infini d'instances de fausseté (différentes).

Cela dit, la plupart des types intégrés renvoient leur valeur falsifiée lorsque leur constructeur est appelé sans arguments:

>>> str()
''
>>> complex()
0j
>>> list()
[]
64
Aran-Fey

Non, et en général, cette valeur peut ne pas exister. Le Python modèle de données est assez vague sur la façon dont la valeur de vérité d'un type peut être implémentée:

object.__bool__(self)

Appelé pour implémenter les tests de valeur de vérité et l'opération intégrée bool (); devrait retourner Faux ou Vrai. Lorsque cette méthode n'est pas définie, __len__() est appelée, si elle est définie, et l'objet est considéré comme vrai si son résultat n'est pas nul. Si une classe ne définit ni __len__() ni __bool__(), toutes ses instances sont considérées comme vraies.

Considérez donc:

import random
class Wacky:
    def __bool__(self):
        return bool(random.randint(0,1))

Que doit renvoyer f(Wacky)?

26
juanpa.arrivillaga

Tous les types n'ont pas une telle valeur pour commencer. D'autres peuvent avoir plusieurs de ces valeurs. La façon la plus correcte de le faire serait de créer un dict de type-à-valeur, car alors vous pourriez vérifier si un type donné était dans le dict, et vous pouvez choisir quelle valeur est la bonne s'il y a plusieurs options . L'inconvénient est bien sûr que vous devrez en quelque sorte enregistrer chaque type qui vous intéresse.

Alternativement, vous pouvez écrire une fonction en utilisant des heuristiques. Si vous faisiez très attention à ce que vous avez passé dans la fonction, cela serait probablement d'une utilité limitée. Par exemple, tous les cas que vous affichez à l'exception de complex sont des conteneurs qui se généralisent avec cls().

complex fonctionne en fait comme ça aussi, mais je le mentionne séparément car int et float ne le font pas. Donc, si votre tentative avec le constructeur vide échoue en renvoyant un objet véridique ou en levant un TypeError, vous pouvez essayer cls(0). Et ainsi de suite...

Mise à jour

@ juanpa.arrivillaga's answer suggère en fait une solution de contournement intelligente qui fonctionnera pour la plupart des classes. Vous pouvez étendre la classe et créer de force une instance qui sera fausse mais sinon identique à la classe d'origine. Vous devez le faire en étendant car les méthodes de dunder comme __bool__ ne sont jamais recherchés que sur la classe, jamais sur une instance. Il existe également de nombreux types où de telles méthodes ne peuvent pas être remplacées sur l'instance pour commencer. Comme le remarque maintenant supprimé d'Aran-Fey, vous pouvez appeler de manière sélective object.__new__ ou t.__new__, selon que vous avez affaire à un cas très spécial (comme int) ou non:

def f(t):
    class tx(t):
        def __bool__(self):
            return False
    try:
        return object.__new__(tx)
    except TypeError:
        return tx.__new__(tx)

Cela ne fonctionnera que pour 99,9% des classes que vous rencontrez. Il est possible de créer un cas artificiel qui déclenche un TypeError lorsqu'il est passé à object.__new__ comme int le fait et ne permet pas une version sans argument de t.__new__, mais je doute que vous trouverez jamais une telle chose dans la nature. Voir Gist @ Aran-Fey fait pour le démontrer.

5
Mad Physicist

Ceci est en fait appelé un élément d'identité , et en programmation est le plus souvent considéré comme faisant partie de la définition d'un monoïde . En python, vous pouvez l'obtenir pour un type en utilisant la fonction mzero dans le package PyMonad . Haskell l'appelle mempty .

4
Karl Bielefeldt

Une telle fonction n'existe pas car elle n'est pas possible en général. Une classe peut ne pas avoir de valeur falsifiée ou il peut être nécessaire d'inverser une implémentation arbitrairement complexe de __bool__.

Ce que vous pourriez faire en cassant tout le reste, c'est construire un nouvel objet de cette classe et assigner de force son __bool__ fonction à une fonction qui renvoie False. Bien que je soupçonne que vous recherchez un objet qui autrement serait un membre valide de la classe.

En tout cas, c'est une très mauvaise idée de style classique.

2
Kafein