que propias procedimientos por parametros metodos funciones ejemplos defecto comando python default-value namedtuple optional-arguments

propias - que es*args en python



nombrados como valores predeterminados y de tupla para argumentos de palabras clave opcionales (21)

Python 3.7

Utilice el parámetro por defecto .

>>> from collections import namedtuple >>> fields = (''val'', ''left'', ''right'') >>> Node = namedtuple(''Node'', fields, defaults=(None,) * len(fields)) >>> Node() Node(val=None, left=None, right=None)

Antes de Python 3.7

Establezca Node.__new__.__defaults__ en los valores predeterminados.

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

Antes de Python 2.6

Establezca Node.__new__.func_defaults en los valores predeterminados.

>>> from collections import namedtuple >>> Node = namedtuple(''Node'', ''val left right'') >>> Node.__new__.func_defaults = (None,) * len(Node._fields) >>> Node() Node(val=None, left=None, right=None)

Orden

En todas las versiones de Python, si establece menos valores predeterminados que los que existen en el grupo nombrado, los valores predeterminados se aplican a los parámetros más a la derecha. Esto le permite mantener algunos argumentos como argumentos requeridos.

>>> Node.__new__.__defaults__ = (1,2) >>> Node() Traceback (most recent call last): ... TypeError: __new__() missing 1 required positional argument: ''val'' >>> Node(3) Node(val=3, left=1, right=2)

Envoltura para Python 2.6 a 3.6

Aquí hay una envoltura para usted, que incluso le permite (opcionalmente) establecer los valores predeterminados en algo distinto a None . Esto no soporta los argumentos requeridos.

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

Ejemplo:

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

Estoy tratando de convertir una clase de "datos" huecos 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 a namedtuple parece:

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 hizo cargo del valor predeterminado utilizando valores predeterminados para los argumentos nombrados / palabras clave. Algo como:

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

Pero esto no funciona en el caso de mi tupla nombrada refactorizada, ya que espera que pase todos los campos. Por supuesto, puedo reemplazar las ocurrencias de Node(val) a 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 debo tragar la píldora y seguir adelante con la "búsqueda y reemplazo"? :)


Aquí hay una respuesta genérica corta y simple con una buena sintaxis para una tupla nombrada con argumentos predeterminados:

import collections def dnamedtuple(typename, field_names, **defaults): fields = sorted(field_names.split(), key=lambda x: x in defaults) T = collections.namedtuple(typename, '' ''.join(fields)) T.__new__.__defaults__ = tuple(defaults[field] for field in fields[-len(defaults):]) return T

Uso:

Test = dnamedtuple(''Test'', ''one two three'', two=2) Test(1, 3) # Test(one=1, three=3, two=2)

Minificado

def dnamedtuple(tp, fs, **df): fs = sorted(fs.split(), key=df.__contains__) T = collections.namedtuple(tp, '' ''.join(fs)) T.__new__.__defaults__ = tuple(df[i] for i in fs[-len(df):]) return T


Aquí hay una versión más compacta inspirada en la respuesta de justinfay:

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


Aquí hay una versión menos flexible, pero más concisa de la envoltura de Mark Lodato: toma los campos y los valores predeterminados como un diccionario.

import collections def namedtuple_with_defaults(typename, fields_dict): T = collections.namedtuple(typename, '' ''.join(fields_dict.keys())) T.__new__.__defaults__ = tuple(fields_dict.values()) return T

Ejemplo:

In[1]: fields = {''val'': 1, ''left'': 2, ''right'':3} In[2]: Node = namedtuple_with_defaults(''Node'', fields) In[3]: Node() Out[3]: Node(val=1, left=2, right=3) In[4]: Node(4,5,6) Out[4]: Node(val=4, left=5, right=6) In[5]: Node(val=10) Out[5]: Node(val=10, left=2, right=3)


Combinando enfoques de @Denis y @Mark:

from collections import namedtuple import inspect class Node(namedtuple(''Node'', ''left right val'')): __slots__ = () def __new__(cls, *args, **kwargs): args_list = inspect.getargspec(super(Node, cls).__new__).args[len(args)+1:] params = {key: kwargs.get(key) for key in args_list + kwargs.keys()} return super(Node, cls).__new__(cls, *args, **params)

Eso debería permitir crear la tupla con argumentos posicionales y también con casos mixtos. Casos de prueba:

>>> print Node() Node(left=None, right=None, val=None) >>> print Node(1,2,3) Node(left=1, right=2, val=3) >>> print Node(1, right=2) Node(left=1, right=2, val=None) >>> print Node(1, right=2, val=100) Node(left=1, right=2, val=100) >>> print Node(left=1, right=2, val=100) Node(left=1, right=2, val=100) >>> print Node(left=1, right=2) Node(left=1, right=2, val=None)

pero también soporta TypeError:

>>> Node(1, left=2) TypeError: __new__() got multiple values for keyword argument ''left''


Con typing.NamedTuple en Python 3.6.1+ puede proporcionar tanto un valor predeterminado como una anotación de tipo a un campo NamedTuple. Usa la typing.Any si solo necesitas lo primero:

from typing import Any, NamedTuple class Node(NamedTuple): val: Any left: ''Node'' = None right: ''Node'' = None

Uso:

>>> Node(1) Node(val=1, left=None, right=None) >>> n = Node(1) >>> Node(2, left=n) Node(val=2, left=Node(val=1, left=None, right=None), right=None)

Además, en caso de que necesite tanto valores predeterminados como mutabilidad opcional, Python 3.7 tendrá clases de datos (PEP 557) que, en algunos (¿muchos?) Casos, pueden reemplazar las muestras nombradas.

Nota: una peculiaridad de la especificación actual de annotations (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 * . Por lo tanto, dado que "los nombres de clase se definen una vez que se ha ejecutado todo el cuerpo de la clase", las anotaciones para ''Node'' en los campos de clase anteriores deben ser cadenas para evitar NameError.

Este tipo de sugerencias de tipo se llama "referencia hacia adelante" ( [1] , [2] ), y con PEP 563, Python 3.7+ tendrá una importación de __future__ (que se habilitará de forma predeterminada en 4.0) que permitirá usar la función de reenvío Referencias sin comillas, aplazando su evaluación.

* Solo las anotaciones de variables locales de AFAICT no se evalúan en tiempo de ejecución. (fuente: PEP 526 )


Corto, simple, y no lleva a la gente a usar su isinstance inapropiada:

class Node(namedtuple(''Node'', (''val'', ''left'', ''right''))): @classmethod def make(cls, val, left=None, right=None): return cls(val, left, right) # Example x = Node.make(3) x._replace(right=Node.make(4))


En Python3.7 + hay un nuevo valor por defaults= argumento de palabra clave.

los valores predeterminados pueden ser None o una iterable de los valores predeterminados. Dado que los campos con un valor predeterminado deben aparecer después de cualquier campo sin un valor predeterminado, los valores predeterminados se aplican a los parámetros más a la derecha. Por ejemplo, si los nombres de campo son [''x'', ''y'', ''z''] y los valores predeterminados son (1, 2) , entonces x será un argumento requerido, y defecto será 1 , z defecto será 2 .

Ejemplo de uso:

$ ./python Python 3.7.0b1+ (heads/3.7:4d65430, Feb 1 2018, 09:28:35) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from collections import namedtuple >>> nt = namedtuple(''nt'', (''a'', ''b'', ''c''), defaults=(1, 2)) >>> nt(0) nt(a=0, b=1, c=2) >>> nt(0, 3) nt(a=0, b=3, c=2) >>> nt(0, c=3) nt(a=0, b=1, c=3)


Encuentro esta versión más fácil de leer:

from collections import namedtuple def my_tuple(**kwargs): defaults = { ''a'': 2.0, ''b'': True, ''c'': "hello", } default_tuple = namedtuple(''MY_TUPLE'', '' ''.join(defaults.keys()))(*defaults.values()) return default_tuple._replace(**kwargs)

Esto no es tan eficiente como requiere la creación del objeto dos veces, pero puede cambiarlo definiendo la duplicación predeterminada dentro del módulo y haciendo que la función realice la línea de reemplazo.


Envuélvelo en una función.

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


Este es un ejemplo directamente de la documentación :

Los valores predeterminados se pueden implementar utilizando _replace () para personalizar una instancia de prototipo:

>>> Account = namedtuple(''Account'', ''owner balance transaction_count'') >>> default_account = Account(''<owner name>'', 0.0, 0) >>> johns_account = default_account._replace(owner=''John'') >>> janes_account = default_account._replace(owner=''Jane'')

Entonces, el ejemplo del OP sería:

from collections import namedtuple Node = namedtuple(''Node'', ''val left right'') default_node = Node(None, None, None) example = default_node._replace(val="whut")

Sin embargo, me gustan más algunas de las otras respuestas dadas aquí. Sólo quería añadir esto para completar.


Inspirado por esta respuesta a una pregunta diferente, aquí está mi solución propuesta basada en una metaclass y usando super (para manejar el subcálculo futuro correctamente). Es bastante similar a share .

from collections import namedtuple NodeTuple = namedtuple("NodeTuple", ("val", "left", "right")) class NodeMeta(type): def __call__(cls, val, left=None, right=None): return super(NodeMeta, cls).__call__(val, left, right) class Node(NodeTuple, metaclass=NodeMeta): __slots__ = ()

Entonces:

>>> Node(1, Node(2, Node(4)),(Node(3, None, Node(5)))) Node(val=1, left=Node(val=2, left=Node(val=4, left=None, right=None), right=None), right=Node(val=3, left=None, right=Node(val=5, left=None, right=None)))


La respuesta de jterrace para usar recordtype es excelente, pero el autor de la biblioteca recomienda usar su proyecto namedlist , que proporciona namedlist tanto mutables (lista namedlist ) como inmutables (nombre namedtuple ).

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


No estoy seguro de si hay una manera fácil con solo el par incorporado nombrado. Hay un módulo agradable llamado recordtype que tiene esta funcionalidad:

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


Otra solución:

import collections def defaultargs(func, defaults): def wrapper(*args, **kwargs): for key, value in (x for x in defaults[len(args):] if len(x) == 2): kwargs.setdefault(key, value) return func(*args, **kwargs) return wrapper def namedtuple(name, fields): NamedTuple = collections.namedtuple(name, [x[0] for x in fields]) NamedTuple.__new__ = defaultargs(NamedTuple.__new__, [(NamedTuple,)] + fields) return NamedTuple

Uso:

>>> Node = namedtuple(''Node'', [ ... (''val'',), ... (''left'', None), ... (''right'', None), ... ]) __main__.Node >>> Node(1) Node(val=1, left=None, right=None) >>> Node(1, 2, right=3) Node(val=1, left=2, right=3)


Python 3.7: introducción de defaults en la definición de duplicados.

Ejemplo como se muestra en la documentación:

>>> Account = namedtuple(''Account'', [''type'', ''balance''], defaults=[0]) >>> Account._fields_defaults {''balance'': 0} >>> Account(''premium'') Account(type=''premium'', balance=0)

Lea más here .


Sub-asigné un nombre a tu pareja y __new__ método __new__ :

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

Esto preserva una jerarquía de tipos intuitiva, que la creación de una función de fábrica disfrazada como una clase no hace.


También puedes usar esto:

import inspect def namedtuple_with_defaults(type, default_value=None, **kwargs): args_list = inspect.getargspec(type.__new__).args[1:] params = dict([(x, default_value) for x in args_list]) params.update(kwargs) return type(**params)

Básicamente, esto le da la posibilidad de construir cualquier tupla con un valor predeterminado y anular solo los parámetros que necesita, por ejemplo:

import collections Point = collections.namedtuple("Point", ["x", "y"]) namedtuple_with_defaults(Point) >>> Point(x=None, y=None) namedtuple_with_defaults(Point, x=1) >>> Point(x=1, y=None)


Un ejemplo ligeramente extendido para inicializar todos los argumentos que faltan con None :

from collections import namedtuple class Node(namedtuple(''Node'', [''value'', ''left'', ''right''])): __slots__ = () def __new__(cls, *args, **kwargs): # initialize missing kwargs with None all_kwargs = {key: kwargs.get(key) for key in cls._fields} return super(Node, cls).__new__(cls, *args, **all_kwargs)


Usando la clase NamedTuple de mi biblioteca Advanced Enum (aenum) , y usando la sintaxis de la class , esto es bastante simple:

from aenum import NamedTuple class Node(NamedTuple): val = 0 left = 1, ''previous Node'', None right = 2, ''next Node'', None

El único inconveniente potencial es el requisito de una cadena __doc__ para cualquier atributo con un valor predeterminado (es opcional para los atributos simples). En uso parece:

>>> Node() Traceback (most recent call last): ... TypeError: values not provided for field(s): val >>> Node(3) Node(val=3, left=None, right=None)

Las ventajas que esto tiene sobre justinfay''s answer :

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

es la simplicidad, además de estar basada en metaclass lugar de basada en exec .


Ya que está usando namedtuple como clase de datos, debe tener en cuenta que Python 3.7 introducirá un decorador @dataclass para este propósito, y por supuesto tiene valores predeterminados.

Un ejemplo de la documentación :

@dataclass class C: a: int # ''a'' has no default value b: int = 0 # assign a default value for ''b''

Mucho más limpio, legible y utilizable que el hacking namedtuple . No es difícil predecir que el uso de namedtuple s se reducirá con la adopción de 3.7.