nametuple và các giá trị mặc định cho các đối số từ khóa tùy chọn


300

Tôi đang cố gắng chuyển đổi một lớp "dữ liệu" rỗng dài thành một tuple có tên. Lớp của tôi hiện tại trông như thế này:

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

Sau khi chuyển đổi, namedtuplenó trông giống như:

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

Nhưng có một vấn đề ở đây. Lớp ban đầu của tôi cho phép tôi chuyển vào chỉ một giá trị và chăm sóc mặc định bằng cách sử dụng các giá trị mặc định cho các đối số từ khóa / tên. Cái gì đó như:

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

Nhưng điều này không hoạt động trong trường hợp bộ tái cấu trúc của tôi có tên là tuple vì nó hy vọng tôi sẽ vượt qua tất cả các lĩnh vực. Tôi tất nhiên có thể thay thế các lần xuất hiện của Node(val)để Node(val, None, None)nhưng nó không phải là theo ý thích của tôi.

Vì vậy, có tồn tại một mẹo hay có thể giúp tôi viết lại thành công mà không cần thêm nhiều phức tạp mã (siêu lập trình) hay tôi chỉ nên nuốt viên thuốc và tiếp tục với "tìm kiếm và thay thế"? :)


2
Tại sao bạn muốn thực hiện chuyển đổi này? Tôi thích Nodelớp học ban đầu của bạn . Tại sao chuyển đổi sang tuple được đặt tên?
steveha

34
Tôi muốn thực hiện chuyển đổi này vì Nodecác lớp hiện tại và các lớp khác là các đối tượng giá trị giữ dữ liệu đơn giản với một loạt các trường khác nhau ( Nodechỉ là một trong số chúng). Các khai báo lớp này không có gì nhiều ngoài nhiễu dòng IMHO do đó muốn cắt bỏ chúng. Tại sao phải duy trì một cái gì đó không cần thiết? :)
sasuke

Bạn không có bất kỳ chức năng phương thức nào trên các lớp học của bạn cả? Bạn không, ví dụ, có một .debug_print()phương pháp đi trên cây và in nó?
steveha

2
Chắc chắn tôi làm, nhưng đó là cho BinaryTreelớp học. Nodevà các chủ sở hữu dữ liệu khác không yêu cầu các phương thức đặc biệt như vậy, đặc biệt là các bộ dữ liệu được đặt tên có một đại diện __str__và phong nha __repr__. :)
sasuke

Được rồi, có vẻ hợp lý. Và tôi nghĩ Ignacio Vazquez-Abrams đã cho bạn câu trả lời: sử dụng một hàm thực hiện các giá trị mặc định cho nút của bạn.
steveha

Câu trả lời:


532

Python 3.7

Sử dụng tham số mặc định .

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

Hoặc tốt hơn nữa, sử dụng thư viện dataclass mới , đẹp hơn nhiều so với têntuple.

>>> from dataclasses import dataclass
>>> from typing import Any
>>> @dataclass
... class Node:
...     val: Any = None
...     left: 'Node' = None
...     right: 'Node' = None
>>> Node()
Node(val=None, left=None, right=None)

Trước Python 3.7

Đặt Node.__new__.__defaults__thành các giá trị mặc định.

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

Trước Python 2.6

Đặt Node.__new__.func_defaultsthành các giá trị mặc định.

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

Đặt hàng

Trong tất cả các phiên bản của Python, nếu bạn đặt ít giá trị mặc định hơn tồn tại trong tên được đặt tên, mặc định được áp dụng cho các tham số ngoài cùng bên phải. Điều này cho phép bạn giữ một số đối số như các đối số cần thiết.

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

Trình bao bọc cho Python 2.6 đến 3.6

Đây là một trình bao bọc cho bạn, thậm chí cho phép bạn (tùy chọn) đặt các giá trị mặc định thành một giá trị khác None. Điều này không hỗ trợ các đối số cần thiết.

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

Thí dụ:

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

22
Hãy xem ... một câu trả lời của bạn: a) là câu trả lời ngắn nhất / đơn giản nhất, b) bảo toàn hiệu quả không gian, c) không phá vỡ isinstance... tất cả ưu, không có khuyết điểm ... quá tệ là bạn đã hơi muộn Bữa tiệc. Đây là câu trả lời tốt nhất.
Gerrat

1
Một vấn đề với phiên bản trình bao bọc: không giống như các bộ sưu tập dựng sẵn.namedtuple, phiên bản này không được tuần tự hóa / đa xử lý nếu tuần tự def () được bao gồm trong một mô-đun khác.
Michael Scott Cuthbert

2
Tôi đã đưa ra câu trả lời này một upvote vì nó là tốt hơn so với của riêng tôi. Thật đáng tiếc tuy nhiên câu trả lời của riêng tôi cứ bị thu hút: |
Justin Fay

3
@ishaaq, vấn đề (None)không phải là một tuple, nó None. Nếu bạn sử dụng (None,)thay thế, nó sẽ hoạt động tốt.
Đánh dấu Lodato

2
Thông minh! Bạn có thể khái quát cài đặt mặc định bằng:Node.__new__.__defaults__= (None,) * len(Node._fields)
ankostis

142

Tôi phân lớp tên làupuple và ghi đè __new__phương thức:

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)

Điều này bảo tồn một hệ thống phân cấp kiểu trực quan, mà việc tạo ra một chức năng của nhà máy được ngụy trang thành một lớp thì không.


7
Điều này có thể cần các thuộc tính vị trí và trường để duy trì hiệu quả không gian của bộ dữ liệu được đặt tên.
Pepijn

Vì một số lý do, __new__không được gọi khi _replaceđược sử dụng.

1
Vui lòng xem câu trả lời @ marc-lodato dưới đây mà IMHO là giải pháp tốt hơn thế này.
Justin Fay

1
nhưng câu trả lời của @ marc-lodato không cung cấp khả năng cho một lớp con có các mặc định khác nhau
Jason S

1
@JasonS, tôi nghi ngờ rằng đối với một lớp con có các mặc định khác nhau có thể vi phạm LSP . Tuy nhiên, một lớp con rất có thể muốn có nhiều mặc định hơn . Trong mọi trường hợp, lớp con sẽ sử dụng phương thức của justinfay và lớp cơ sở sẽ ổn với phương thức của Marc .
Alexey

94

Bọc nó trong một chức năng.

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

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

15
Điều này là thông minh, và có thể là một lựa chọn tốt, nhưng cũng có thể gây ra vấn đề bằng cách phá vỡ isinstance(Node('val'), Node): bây giờ nó sẽ đưa ra một ngoại lệ, thay vì trả về True. Mặc dù dài dòng hơn một chút, câu trả lời của @ justinfay (bên dưới) giữ đúng thông tin phân cấp loại, vì vậy có lẽ là cách tiếp cận tốt hơn nếu những người khác sẽ tương tác với các phiên bản Node.
Gabriel Grant

4
Tôi thích sự ngắn gọn của câu trả lời này. Có lẽ mối quan tâm trong nhận xét trên có thể được giải quyết bằng cách đặt tên hàm def make_node(...):thay vì giả vờ nó là một định nghĩa lớp. Theo cách đó, người dùng không muốn kiểm tra tính đa hình loại trên hàm mà sử dụng chính định nghĩa tuple.
dùng1556435

Xem câu trả lời của tôi cho một biến thể của điều này mà không phải chịu sự hiểu lầm của mọi người để sử dụng isinstancekhông chính xác.
Elliot Cameron

70

Với typing.NamedTuplePython 3.6.1+, bạn có thể cung cấp cả giá trị mặc định và chú thích kiểu cho trường NamedTuple. Sử dụng typing.Anynếu bạn chỉ cần cái trước:

from typing import Any, NamedTuple


class Node(NamedTuple):
    val: Any
    left: 'Node' = None
    right: 'Node' = None

Sử dụng:

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

Ngoài ra, trong trường hợp bạn cần cả giá trị mặc định và khả năng biến đổi tùy chọn, Python 3.7 sẽ có các lớp dữ liệu (PEP 557) có thể trong một số trường hợp (nhiều?) Thay thế các tên được đặt tên.


Sidenote: một điều không rõ về đặc tả hiện tại của các chú thích (biểu thức sau :cho tham số và biến và sau ->cho hàm) trong Python là chúng được đánh giá tại thời điểm định nghĩa * . Vì vậy, vì "tên lớp được xác định một khi toàn bộ phần thân của lớp đã được thực thi", các chú thích 'Node'trong các trường lớp ở trên phải là các chuỗi để tránh NameError.

Loại gợi ý loại này được gọi là "tham chiếu chuyển tiếp" ( [1] , [2] ) và với PEP 563 Python 3.7+ sẽ có một __future__nhập (được bật theo mặc định trong 4.0) sẽ cho phép sử dụng các tham chiếu chuyển tiếp không có trích dẫn, hoãn đánh giá của họ.

* Chỉ AFAICT chú thích biến cục bộ không được đánh giá trong thời gian chạy. (nguồn: PEP 526 )


4
Đây có vẻ là giải pháp sạch nhất cho người dùng 3.6.1+. Lưu ý rằng ví dụ này (hơi) khó hiểu là gợi ý loại cho các trường leftright(nghĩa là Node) cùng loại với lớp được định nghĩa và do đó phải được viết dưới dạng chuỗi.
101

1
@ 101, cảm ơn bạn, tôi đã thêm một lưu ý về điều này vào câu trả lời.
nhà sư thời gian

2
Tương tự cho thành ngữ là my_list: List[T] = None self.my_list = my_list if my_list is not None else []gì? Chúng ta không thể sử dụng các tham số mặc định như thế này?
weberc2

@ weberc2 Câu hỏi tuyệt vời! Tôi không chắc chắn nếu cách giải quyết này cho def có thể thay đổi. giá trị là có thể với typing.NamedTuple. Nhưng với các lớp dữ liệu, bạn có thể sử dụng Field các đối tượng với một default_factoryattr. cho điều này, thay thế thành ngữ của bạn với my_list: List[T] = field(default_factory=list).
nhà sư thời gian

20

Đây là một ví dụ trực tiếp từ các tài liệu :

Các giá trị mặc định có thể được triển khai bằng cách sử dụng _replace () để tùy chỉnh một thể hiện nguyên mẫu:

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

Vì vậy, ví dụ của OP sẽ là:

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

Tuy nhiên, tôi thích một số câu trả lời khác được đưa ra ở đây tốt hơn. Tôi chỉ muốn thêm điều này cho đầy đủ.


2
+1. Thật kỳ lạ khi họ quyết định thực hiện một _phương pháp (về cơ bản có nghĩa là một phương thức riêng tư) cho một thứ giống như replacecó vẻ khá hữu ích ..
sasuke

@sasuke - Mình cũng thắc mắc điều đó. Thay vào đó, có một chút kỳ lạ khi bạn xác định các thành phần bằng một chuỗi được phân tách bằng dấu cách *args. Nó có thể chỉ là nó đã được thêm vào ngôn ngữ trước khi rất nhiều những điều đó được tiêu chuẩn hóa.
Tim Tonomall

12
Các _tiền tố là để tránh va chạm với những cái tên của người sử dụng định nghĩa lĩnh vực tuple (liên quan doc quote: "Bất cứ hợp lệ Python định danh có thể được sử dụng cho một fieldname ngoại trừ tên bắt đầu với dấu gạch dưới."). Đối với chuỗi phân tách không gian, tôi nghĩ rằng đó chỉ là để lưu một vài tổ hợp phím (và bạn có thể truyền một chuỗi các chuỗi nếu bạn thích).
Søren Løvborg

1
À, ừ, tôi quên bạn truy cập vào các yếu tố của bộ dữ liệu được đặt tên làm thuộc tính, vì vậy điều này _rất có ý nghĩa sau đó.
Tim Tonomall

2
Giải pháp của bạn rất đơn giản và tốt nhất. Phần còn lại là IMHO khá xấu xí. Tôi sẽ chỉ làm một thay đổi nhỏ. Thay vì default_node tôi thích node_default hơn vì nó giúp trải nghiệm tốt hơn với IntelliSense. Trong trường hợp bạn bắt đầu nhập nút, bạn đã nhận được mọi thứ bạn cần :)
Pavel Hanpari

19

Tôi không chắc có cách nào dễ dàng chỉ với cái tên được tích hợp sẵn hay không. Có một mô-đun đẹp gọi là recordtype có chức năng này:

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

2
Ah, không thể sử dụng gói bên thứ ba mặc dù recordtypechắc chắn có vẻ thú vị cho công việc trong tương lai. +1
sasuke

Mô-đun này khá nhỏ và chỉ có một tệp duy nhất để bạn luôn có thể thêm nó vào dự án của mình.
jterrace

Đủ công bằng, mặc dù tôi sẽ chờ thêm một thời gian nữa cho một giải pháp tuple thuần có tên là có một giải pháp ngoài kia trước khi đánh dấu điều này được chấp nhận! :)
sasuke

Đồng ý trăn thuần sẽ tốt, nhưng tôi không nghĩ có một :(
jterrace

3
Chỉ cần lưu ý rằng recordtypecó thể thay đổi trong khi namedtuplekhông. Điều này có thể quan trọng nếu bạn muốn đối tượng có thể băm được (mà tôi đoán là bạn không biết, vì nó bắt đầu như một lớp học).
bavaza

14

Đây là một phiên bản nhỏ gọn hơn lấy cảm hứng từ câu trả lời của justinfay:

from collections import namedtuple
from functools import partial

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

7
Coi chừng Node(1, 2)không hoạt động với công thức này, nhưng hoạt động trong câu trả lời của @ justinfay. Mặt khác, nó khá tiện lợi (+1).
jorgeca

12

Trong python3.7 + có một mặc định hoàn toàn mới = đối số từ khóa.

mặc định có thể Nonehoặc lặp lại các giá trị mặc định. Vì các trường có giá trị mặc định phải đến sau bất kỳ trường nào không có mặc định, mặc định được áp dụng cho các tham số ngoài cùng bên phải. Ví dụ: nếu tên trường là ['x', 'y', 'z']và mặc định là (1, 2), thì đó xsẽ là một đối số bắt buộc, ysẽ được mặc định 1zsẽ mặc định là 2.

Ví dụ sử dụng:

$ ./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)

7

Ngắn gọn, đơn giản và không khiến mọi người sử dụng isinstancekhông đúng cách:

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

5

Một ví dụ mở rộng một chút để khởi tạo tất cả các đối số còn thiếu với 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)

5

Python 3.7: giới thiệu defaultsparam trong định nghĩa têntuple.

Ví dụ như thể hiện trong tài liệu:

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

Đọc thêm ở đây .


4

Bạn cũng có thể sử dụng điều này:

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)

Điều này về cơ bản cung cấp cho bạn khả năng xây dựng bất kỳ bộ dữ liệu có tên nào với giá trị mặc định và ghi đè chỉ các tham số bạn cần, ví dụ:

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)

4

Kết hợp các cách tiếp cận của @Denis và @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) 

Điều đó sẽ hỗ trợ việc tạo bộ dữ liệu với các đối số vị trí và cả với các trường hợp hỗn hợp. Các trường hợp thử nghiệm:

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

nhưng cũng hỗ trợ TypeError:

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

3

Tôi thấy phiên bản này dễ đọc hơn:

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)

Điều này không hiệu quả vì nó yêu cầu tạo đối tượng hai lần nhưng bạn có thể thay đổi điều đó bằng cách xác định bản sao mặc định bên trong mô-đun và chỉ cần có chức năng thực hiện dòng thay thế.


3

Vì bạn đang sử dụng namedtuplenhư một lớp dữ liệu, bạn nên biết rằng python 3.7 sẽ giới thiệu một trình @dataclasstrang trí cho mục đích này - và tất nhiên nó có các giá trị mặc định.

Một ví dụ từ các tài liệu :

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

Sạch sẽ hơn, dễ đọc và có thể sử dụng hơn hack namedtuple. Không khó để dự đoán rằng việc sử dụng namedtuples sẽ giảm khi áp dụng 3.7.


2

Lấy cảm hứng từ câu trả lời này cho một câu hỏi khác, đây là giải pháp được đề xuất của tôi dựa trên siêu dữ liệu và sử dụng super(để xử lý chính xác các phân nhóm trong tương lai). Nó khá giống với câu trả lời của justinfay .

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__ = ()

Sau đó:

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

2

Câu trả lời của jterrace để sử dụng recordtype là rất tốt, nhưng tác giả của thư viện khuyên bạn nên sử dụng dự án danh sách tên của mình , cung cấp cả hai triển khai có thể thay đổi ( namedlist) và bất biến ( 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)

1

Đây là một câu trả lời chung chung, đơn giản với một cú pháp hay cho một bộ dữ liệu có tên với các đối số mặc định:

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

Sử dụng:

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

Giảm thiểu:

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

0

Sử dụng NamedTuplelớp từ Advanced Enum (aenum)thư viện của tôi và sử dụng classcú pháp, điều này khá đơn giản:

from aenum import NamedTuple

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

Một nhược điểm tiềm năng là yêu cầu về một __doc__chuỗi cho bất kỳ thuộc tính nào có giá trị mặc định (tùy chọn này cho các thuộc tính đơn giản). Trong sử dụng, nó trông giống như:

>>> Node()
Traceback (most recent call last):
  ...
TypeError: values not provided for field(s): val

>>> Node(3)
Node(val=3, left=None, right=None)

Những lợi thế này có hơn 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)

là sự đơn giản, cũng như metaclassdựa trên thay vì execdựa.


0

Giải pháp khác:

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

Sử dụng:

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

-1

Đây là một phiên bản ít linh hoạt hơn, nhưng ngắn gọn hơn của trình bao bọc của Mark Lodato: Nó lấy các trường và mặc định làm từ điển.

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

Thí dụ:

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)

4
dictkhông có bảo đảm đặt hàng.
Ethan Furman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.