Estoy tratando de convertir una clase de "datos" hueca alargada en una tupla con nombre. Mi clase actualmente se ve así:
class Node(object):
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
Después de la conversión namedtuplese ve así:
from collections import namedtuple
Node = namedtuple('Node', 'val left right')
Pero hay un problema aquí. Mi clase original me permitió pasar solo un valor y se encargó del valor predeterminado mediante el uso de valores predeterminados para los argumentos con nombre / palabra clave. Algo como:
class BinaryTree(object):
def __init__(self, val):
self.root = Node(val)
Pero esto no funciona en el caso de mi tupla refactorizada, ya que espera que pase todos los campos. Por supuesto, puedo reemplazar las ocurrencias de Node(val)to Node(val, None, None)pero no es de mi agrado.
Entonces, ¿existe un buen truco que pueda hacer que mi reescritura sea exitosa sin agregar mucha complejidad de código (metaprogramación) o debería simplemente tragarme la píldora y continuar con la "búsqueda y reemplazo"? :)

Nodeclase original tal como es. ¿Por qué convertir a tupla nombrada?Nodeclases actuales y otras son simples objetos de valor de titular de datos con un montón de campos diferentes (Nodees solo uno de ellos). Estas declaraciones de clase no son más que ruido de línea en mi humilde opinión, por lo que quería recortarlas. ¿Por qué mantener algo que no se requiere? :).debug_print()método que recorra el árbol y lo imprima?BinaryTreeclase.Nodey otros titulares de los datos no requieren este tipo de métodos especiales especialmente teniendo en cuenta que el nombre tuplas tienen una decente__str__y__repr__la representación. :)Respuestas:
Python 3.7
Use el parámetro predeterminado .
O mejor aún, use la nueva biblioteca de clases de datos , que es mucho más agradable que namedtuple.
Antes de Python 3.7
Establecer
Node.__new__.__defaults__a los valores predeterminados.Antes de Python 2.6
Establecer
Node.__new__.func_defaultsa los valores predeterminados.Orden
En todas las versiones de Python, si establece menos valores predeterminados que los que existen en la tupla nombrada, los valores predeterminados se aplican a los parámetros más a la derecha. Esto le permite mantener algunos argumentos como argumentos requeridos.
Envoltorio para Python 2.6 a 3.6
Aquí hay un contenedor para usted, que incluso le permite (opcionalmente) establecer los valores predeterminados en algo diferente
None. Esto no es compatible con los argumentos requeridos.Ejemplo:
fuente
isinstance... todos los pros, sin contras ... lástima que llegaste un poco tarde a La fiesta. Esta es la mejor respuesta.(None)no es una tupla, lo esNone. Si lo utiliza(None,), debería funcionar bien.Node.__new__.__defaults__= (None,) * len(Node._fields)Subclasifiqué namedtuple y anulé el
__new__método:Esto conserva una jerarquía de tipos intuitiva, que la creación de una función de fábrica disfrazada de clase no.
fuente
__new__no se llama cuando_replacese usa.Envuélvelo en una función.
fuente
isinstance(Node('val'), Node): ahora generará una excepción, en lugar de devolver True. Si bien es un poco más detallado, la respuesta de @ justinfay (a continuación) conserva la información de la jerarquía de tipos correctamente, por lo que probablemente sea un mejor enfoque si otros van a interactuar con instancias de Node.def make_node(...):lugar de pretender que es una definición de clase. De esa manera, los usuarios no se sienten tentados a verificar el tipo de polimorfismo en la función, sino que usan la definición de tupla misma.isinstanceincorrectamente.Con
typing.NamedTuplePython 3.6.1+ puede proporcionar tanto un valor predeterminado como una anotación de tipo a un campo NamedTuple. Úselotyping.Anysi solo necesita lo primero:Uso:
Además, en caso de que necesite valores predeterminados y mutabilidad opcional, Python 3.7 tendrá clases de datos (PEP 557) que en algunos casos (¿muchos?) Pueden reemplazar a las tuplas con nombre.
Nota al margen: una peculiaridad de la especificación actual de anotaciones (expresiones después
:para parámetros y variables y después->para funciones) en Python es que se evalúan en el momento de la definición * . Entonces, dado que "los nombres de clase se definen una vez que se ha ejecutado todo el cuerpo de la clase", las anotaciones'Node'en los campos de clase anteriores deben ser cadenas para evitar NameError.Este tipo de sugerencias de tipo se denomina "referencia directa" ( [1] , [2] ), y con PEP 563 Python 3.7+ tendrá una
__future__importación (que se habilitará por defecto en 4.0) que permitirá utilizar referencias directas sin comillas, posponiendo su evaluación.* AFAICT solo las anotaciones de variables locales no se evalúan en tiempo de ejecución. (fuente: PEP 526 )
fuente
leftyright(es decirNode) es el mismo tipo que la clase que se está definiendo y, por lo tanto, debe escribirse como cadenas.my_list: List[T] = Noneself.my_list = my_list if my_list is not None else []? ¿No podemos usar parámetros predeterminados como este?typing.NamedTuple. Pero con las clases de datos puede usarFieldobjetos con undefault_factoryatributo. para esto, reemplazando tu idioma conmy_list: List[T] = field(default_factory=list).Este es un ejemplo directamente de los documentos :
Entonces, el ejemplo del OP sería:
Sin embargo, me gustan algunas de las otras respuestas dadas aquí mejor. Solo quería agregar esto para completar.
fuente
_método (que básicamente significa una privada) para algo así comoreplacelo que parece bastante útil ..*args. Puede ser que se haya agregado al lenguaje antes de que muchas de esas cosas fueran estandarizadas._prefijo es evitar chocar con los nombres de los campos de tupla definidos por el usuario (cita relevante del documento: "Cualquier identificador válido de Python puede usarse para un nombre de campo, excepto los nombres que comienzan con un guión bajo"). En cuanto a la cadena separada por espacios, creo que es solo para guardar algunas pulsaciones de teclas (y puede pasar una secuencia de cadenas si lo prefiere)._tiene mucho sentido.No estoy seguro de si hay una manera fácil con solo la tupla nombrada incorporada. Hay un buen módulo llamado tipo de registro que tiene esta funcionalidad:
fuente
recordtypeciertamente parece interesante para el trabajo futuro. +1recordtypees mutable mientrasnamedtupleque no lo es. Esto podría importar si desea que el objeto sea hashable (lo que supongo que no, ya que comenzó como una clase).Aquí hay una versión más compacta inspirada en la respuesta de justinfay:
fuente
Node(1, 2)no funciona con esta receta, pero funciona en la respuesta de @ justinfay. De lo contrario, es bastante ingenioso (+1).En python3.7 + hay un nuevo valor predeterminado = argumento de palabra clave.
Ejemplo de uso:
fuente
Corto, simple y no hace que las personas lo usen
isinstanceincorrectamente:fuente
Un ejemplo ligeramente extendido para inicializar todos los argumentos faltantes con
None:fuente
Python 3.7: introducción de
defaultsparam en la definición de tupla nombrada.Ejemplo como se muestra en la documentación:
Lee más aquí .
fuente
También puedes usar esto:
Básicamente, esto le brinda la posibilidad de construir cualquier tupla con un valor predeterminado y anular solo los parámetros que necesita, por ejemplo:
fuente
Combinando enfoques de @Denis y @Mark:
Eso debería apoyar la creación de la tupla con argumentos posicionales y también con casos mixtos. Casos de prueba:
pero también es compatible con TypeError:
fuente
Encuentro esta versión más fácil de leer:
Esto no es tan eficiente, ya que requiere la creación del objeto dos veces, pero puede cambiarlo definiendo el duplicado predeterminado dentro del módulo y simplemente haciendo que la función haga la línea de reemplazo.
fuente
Como está utilizando
namedtupleuna clase de datos, debe tener en cuenta que python 3.7 presentará un@dataclassdecorador para este mismo propósito, y por supuesto tiene valores predeterminados.Un ejemplo de los documentos :
Mucho más limpio, legible y utilizable que la piratería
namedtuple. No es difícil predecir que el uso denamedtuples disminuirá con la adopción de 3.7.fuente
Inspirado por esta respuesta a una pregunta diferente, aquí está mi solución propuesta basada en una metaclase y usando
super(para manejar correctamente las subcalificaciones futuras). Es bastante similar a la respuesta de justinfay .Luego:
fuente
La respuesta de jterrace para usar el tipo de registro es excelente, pero el autor de la biblioteca recomienda usar su proyecto de lista de nombres , que proporciona implementaciones tanto mutable (
namedlist) como inmutable (namedtuple).fuente
Aquí hay una respuesta genérica corta y simple con una buena sintaxis para una tupla nombrada con argumentos predeterminados:
Uso:
Minified:
fuente
Usando la
NamedTupleclase de miAdvanced Enum (aenum)biblioteca, y usando laclasssintaxis, esto es bastante simple:El único inconveniente potencial es el requisito de una
__doc__cadena para cualquier atributo con un valor predeterminado (es opcional para atributos simples). En uso se ve así:Las ventajas que esto tiene sobre
justinfay's answer:es simplicidad, además de estar
metaclassbasado en lugar de estarexecbasado.fuente
Otra solución:
Uso:
fuente
Aquí hay una versión menos flexible pero más concisa del contenedor de Mark Lodato: toma los campos y los valores predeterminados como un diccionario.
Ejemplo:
fuente
dictNo tiene garantía de pedido.