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 namedtuple
se 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"? :)
Node
clase original tal como es. ¿Por qué convertir a tupla nombrada?Node
clases actuales y otras son simples objetos de valor de titular de datos con un montón de campos diferentes (Node
es 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?BinaryTree
clase.Node
y 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_defaults
a 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_replace
se 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.isinstance
incorrectamente.Con
typing.NamedTuple
Python 3.6.1+ puede proporcionar tanto un valor predeterminado como una anotación de tipo a un campo NamedTuple. Úselotyping.Any
si 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
left
yright
(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] = None
self.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 usarField
objetos con undefault_factory
atributo. 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í comoreplace
lo 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
recordtype
ciertamente parece interesante para el trabajo futuro. +1recordtype
es mutable mientrasnamedtuple
que 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
isinstance
incorrectamente:fuente
Un ejemplo ligeramente extendido para inicializar todos los argumentos faltantes con
None
:fuente
Python 3.7: introducción de
defaults
param 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
namedtuple
una clase de datos, debe tener en cuenta que python 3.7 presentará un@dataclass
decorador 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 denamedtuple
s 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
NamedTuple
clase de miAdvanced Enum (aenum)
biblioteca, y usando laclass
sintaxis, 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
metaclass
basado en lugar de estarexec
basado.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
dict
No tiene garantía de pedido.