389 votes

Nommé n-uplet et mot-clé facultatif arguments

Je suis en train de convertir un allongé creux "données" de la classe dans un nom de tuple. Ma classe actuellement ressemble à ceci:

class Node(object):
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

Après la conversion en namedtuple il ressemble:

from collections import namedtuple
Node = namedtuple('Node', 'val left right')

Mais il y a un problème ici. Ma classe d'origine m'a permis de passer en juste valeur et a pris soin de le par défaut à l'aide des valeurs par défaut pour le nom/mot-clé arguments. Quelque chose comme:

class BinaryTree(object):
    def __init__(self, val):
        self.root = Node(val)

Mais cela ne fonctionne pas dans le cas de mon remaniée nommé tuple, car il s'attend à ce que je passe tous les domaines. Je peux bien sûr remplacer les occurrences de Node(val) de Node(val, None, None) mais il n'est pas à mon goût.

Donc, existe-t-il un bon truc qui peut me ré-écriture de succès sans l'ajout d'un lot de la complexité du code (métaprogrammation) ou dois-je juste avaler la pilule et aller de l'avant avec la "recherche et remplacement"? :)

TIA
-- sauke

703voto

Mark Lodato Points 6548

Ensemble Node.__new__.__defaults__ (ou Node.__new__.func_defaults avant la version 2.6 de Python) pour les valeurs par défaut.

>>> from collections import namedtuple
>>> Node = namedtuple('Node', 'val left right')
>>> Node.__new__.__defaults__ = (None, None, None)

Voici une belle enveloppe pour vous, ce qui vous permet même de vous (facultatif) définissez les valeurs par défaut d'autre chose que de l' None:

import collections
def namedtuple_with_defaults(typename, field_names, default_values=[]):
    T = collections.namedtuple(typename, field_names)
    T.__new__.__defaults__ = (None,) * len(T._fields)
    if isinstance(default_values, collections.Mapping):
        prototype = T(**default_values)
    else:
        prototype = T(*default_values)
    T.__new__.__defaults__ = tuple(prototype)
    return T

Exemple:

>>> Node = namedtuple_with_defaults('Node', 'val left right')
>>> Node()
Node(val=None, left=None, right=None)
>>> Node = namedtuple_with_defaults('Node', 'val left right', [1, 2, 3])
>>> Node()
Node(val=1, left=2, right=3)
>>> Node = namedtuple_with_defaults('Node', 'val left right', {'right':7})
>>> Node()
Node(val=None, left=None, right=7)
>>> Node(4)
Node(val=4, left=None, right=7)

155voto

justinfay Points 731

Je sous-classé namedtuple et emportait l' __new__ méthode:

from collections import namedtuple

class Node(namedtuple('Node', ['value', 'left', 'right'])):
    def __new__(cls, value=None, left=None, right=None):
        return super(Node, cls).__new__(cls, value, left, right)

Cela préserve une interface intuitive de type hiérarchie, qui est la création d'une usine de fonction déguisé en classe ne comprend pas.

104voto

L'envelopper dans une fonction.

NodeT = namedtuple('Node', 'val left right')

def Node(val, left=None, right=None):
  return NodeT(val, left, right)

20voto

jterrace Points 21939

Je ne sais pas si il existe un moyen facile avec juste le haut-namedtuple. Il y a une belle module appelé recordtype qui dispose de cette fonctionnalité:

>>> from recordtype import recordtype
>>> Node = recordtype('Node', [('val', None), ('left', None), ('right', None)])
>>> Node(3)
Node(val=3, left=None, right=None)
>>> Node(3, 'L')
Node(val=3, left=L, right=None)

15voto

Gustav Larsson Points 3416

Voici une version plus compacte inspirée par justinfay de réponse:

from collections import namedtuple
from functools import partial

Node = namedtuple('Node', ('val left right'))
Node.__new__ = partial(Node.__new__, left=None, right=None)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X