वैकल्पिक कीवर्ड तर्कों के लिए नामांकित और डिफ़ॉल्ट मान


300

मैं एक लंबे खोखले "डेटा" वर्ग को नामांकित टुपल में बदलने की कोशिश कर रहा हूं। मेरी कक्षा वर्तमान में इस तरह दिखती है:

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

रूपांतरण के बाद namedtupleऐसा दिखता है:

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

लेकिन यहां एक समस्या है। मेरी मूल कक्षा ने मुझे केवल एक मान में पारित करने की अनुमति दी और नाम / खोजशब्द तर्कों के लिए डिफ़ॉल्ट मानों का उपयोग करके डिफ़ॉल्ट का ध्यान रखा। कुछ इस तरह:

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

लेकिन यह मेरे नामांकित कार्य के मामले में काम नहीं करता है जिसका नाम टपल है क्योंकि यह मुझसे सभी क्षेत्रों को पारित करने की अपेक्षा करता है। मैं निश्चित रूप से की घटनाओं जगह ले सकता है Node(val)के लिए Node(val, None, None)लेकिन यह मेरी पसंद के हिसाब से नहीं है।

तो क्या एक अच्छी चाल मौजूद है, जो बहुत सी कोड जटिलता (मेटाप्रोग्रामिंग) जोड़े बिना मेरे पुन: लिखने को सफल बना सकती है या क्या मुझे बस गोली निगलनी चाहिए और "खोज और प्रतिस्थापित" के साथ आगे बढ़ना चाहिए? :)


2
आप यह रूपांतरण क्यों करना चाहते हैं? मैं आपकी मूल Nodeकक्षा को वैसे ही पसंद करता हूं जैसे वह है। क्यों नामांकित करने के लिए परिवर्तित करें?
स्टीवेहा जूल

34
मैं यह रूपांतरण करना चाहता था क्योंकि वर्तमान Nodeऔर अन्य वर्ग विभिन्न क्षेत्रों के एक समूह के साथ सरल डेटा-धारक मूल्य ऑब्जेक्ट हैं ( Nodeयह उनमें से सिर्फ एक है)। ये वर्ग घोषणाएं लाइन शोर IMHO से ज्यादा कुछ नहीं हैं इसलिए उन्हें बाहर ट्रिम करना चाहते थे। क्यों कुछ है जो आवश्यक नहीं है बनाए रखने के लिए? :)
sasuke

आपके पास अपनी कक्षाओं में कोई विधि कार्य नहीं है? उदाहरण के लिए, आपके पास ऐसा .debug_print()तरीका नहीं है जो पेड़ पर चलता है और उसे प्रिंट करता है?
स्टेव

2
निश्चित रूप से मैं करता हूं, लेकिन यह BinaryTreeवर्ग के लिए है । Nodeऔर अन्य डेटा धारकों को ऐसे विशेष तरीकों की आवश्यकता नहीं होती है जो कि दिए गए टपल्स का एक सभ्य __str__और __repr__प्रतिनिधित्व है। :)
sasuke

ठीक है, उचित लगता है। और मुझे लगता है कि इग्नासियो वाज़केज़-अब्राम्स ने आपको जवाब दिया है: एक फ़ंक्शन का उपयोग करें जो आपके नोड के लिए डिफ़ॉल्ट मान करता है।
स्टेव

जवाबों:


532

पायथन 3.7

डिफ़ॉल्ट पैरामीटर का उपयोग करें ।

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

या बेहतर अभी तक, नए डेटाकालेज़ लाइब्रेरी का उपयोग करें , जो नेमटुपल की तुलना में बहुत अच्छा है।

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

पाइथन 3.7 से पहले

Node.__new__.__defaults__डिफ़ॉल्ट मान पर सेट करें ।

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

पाइथन 2.6 से पहले

Node.__new__.func_defaultsडिफ़ॉल्ट मान पर सेट करें ।

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

गण

पायथन के सभी संस्करणों में, यदि आप नामांकित में मौजूद की तुलना में कम डिफ़ॉल्ट मान सेट करते हैं, तो डिफ़ॉल्ट सबसे सही मापदंडों पर लागू होते हैं। यह आपको आवश्यक तर्क के रूप में कुछ तर्क रखने की अनुमति देता है।

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

पाइथन 2.6 से 3.6 के लिए रैपर

यहां आपके लिए एक रैपर है, जो आपको (वैकल्पिक रूप से) डिफ़ॉल्ट मानों को किसी अन्य चीज़ से सेट करने देता है 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

उदाहरण:

>>> 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
आइए देखते हैं ... आपका वन-लाइनर: ए) सबसे छोटा / सरलतम उत्तर है, बी) अंतरिक्ष दक्षता को बनाए रखता है, ग) टूटता नहीं है isinstance... सभी पेशेवरों, कोई विपक्ष ... बहुत बुरा आप थोड़ा देर से थे पार्टी। यह सबसे अच्छा जवाब है।
गेरेट

1
आवरण संस्करण के साथ एक समस्या: बिलिन कलेक्शंस के विपरीत ।नामलूप, यह संस्करण पिकलेबल / मल्टीप्रोसेस धारावाहिक नहीं है यदि डिफ () एक अलग मॉड्यूल में शामिल है।
माइकल स्कॉट कुथबर्ट

2
मैंने इस उत्तर को एक उत्थान दिया है क्योंकि यह मेरे लिए बेहतर है। इसका अफ़सोस यह है कि मेरा खुद का जवाब उठता रहता है: |
जस्टिन फे

3
@ निश्चय, समस्या यह है कि (None)यह एक तुक नहीं है, यह है None। यदि आप (None,)इसके बजाय उपयोग करते हैं, तो यह ठीक काम करना चाहिए।
मार्क लोदतो

2
अति उत्कृष्ट! आप इसके साथ सेटिंग की चूक को सामान्य कर सकते हैं:Node.__new__.__defaults__= (None,) * len(Node._fields)
akostis

142

मैंने नामांकित को उप-वर्गित किया और __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)

यह एक सहज प्रकार के पदानुक्रम को संरक्षित करता है, जिसे एक वर्ग के रूप में प्रच्छन्न फैक्टरी फ़ंक्शन का निर्माण नहीं करता है।


7
एक नामांकित टपल की अंतरिक्ष दक्षता बनाए रखने के लिए स्लॉट और फ़ील्ड गुणों की आवश्यकता हो सकती है।
पेपिजन

किसी कारण के लिए, __new__जब _replaceउपयोग किया जाता है तो बुलाया नहीं जा रहा है।

1
कृपया नीचे दिए गए @ marc-lodato जवाब पर एक नज़र डालें, जिससे IMHO इससे बेहतर समाधान है।
जस्टिन फे

1
लेकिन @ marc-lodato का जवाब एक उपवर्ग के लिए अलग चूक की क्षमता प्रदान नहीं करता है
जेसन एस

1
@ जैसन, मुझे संदेह है कि एक उपवर्ग के लिए अलग-अलग चूक एलएसपी का उल्लंघन कर सकते हैं । हालाँकि, एक उपवर्ग बहुत अच्छी तरह से अधिक चूक करना चाहता है । किसी भी मामले में, यह उपवर्ग के लिए जस्टिनफे की पद्धति का उपयोग करने के लिए होगा , और मार्क की विधि के साथ आधार वर्ग ठीक होगा ।
एलेक्सी

94

इसे एक फंक्शन में लपेटें।

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

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

15
यह चतुर है, और एक अच्छा विकल्प हो सकता है, लेकिन यह भी टूटने से समस्याएं पैदा कर सकता है isinstance(Node('val'), Node): यह अब एक अपवाद को बढ़ाएगा, बजाय सच्चे लौटने के। जबकि थोड़ा और अधिक क्रिया , @ justinfay का उत्तर (नीचे) टाइप पदानुक्रम जानकारी को ठीक से संरक्षित करता है, इसलिए संभवत: एक बेहतर दृष्टिकोण है यदि अन्य लोग नोड उदाहरणों के साथ बातचीत करने जा रहे हैं।
गेब्रियल ग्रांट

4
मुझे इस उत्तर की संक्षिप्तता पसंद है। शायद ऊपर टिप्पणी में चिंता को फ़ंक्शन का नामकरण करके संबोधित किया जा सकता है def make_node(...):बजाय यह दिखावा करने के कि यह एक वर्ग परिभाषा है। उस तरह से उपयोगकर्ताओं को फ़ंक्शन पर टाइप पॉलीमोर्फिज़्म की जांच करने के लिए लुभाया नहीं जाता है, बल्कि टपल परिभाषा का उपयोग किया जाता है।
user1556435

इसका एक भिन्नता के लिए मेरा जवाब देखें जो isinstanceगलत तरीके से उपयोग करने के लिए भ्रामक लोगों से ग्रस्त नहीं है।
इलियट कैमरन

70

साथ typing.NamedTupleअजगर में 3.6.1+ आप दोनों एक डिफ़ॉल्ट मान और एक NamedTuple क्षेत्र के लिए एक प्रकार का एनोटेशन प्रदान कर सकते हैं। उपयोग करें typing.Anyयदि आपको केवल पूर्व की आवश्यकता है:

from typing import Any, NamedTuple


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

उपयोग:

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

इसके अलावा, यदि आपको डिफ़ॉल्ट मान और वैकल्पिक म्यूटेबिलिटी दोनों की आवश्यकता है, तो Python 3.7 में डेटा क्लासेस (PEP 557) होने वाली हैं जो कुछ (कई?) मामलों में नामांकितों की जगह ले सकती हैं।


Sidenote: की वर्तमान विनिर्देश में से एक मोड़ एनोटेशन (के बाद भाव :मापदंडों और चर के लिए और बाद में ->अजगर में कार्यों के लिए) है कि वे परिभाषा समय में मूल्यांकन किया जाता है है * । इसलिए, चूंकि "कक्षा के नाम को एक बार परिभाषित किया गया है, क्योंकि कक्षा के पूरे शरीर को निष्पादित किया गया है", 'Node'ऊपर दिए गए वर्ग फ़ील्ड के लिए एनोटेशन NameError से बचने के लिए तार होना चाहिए।

इस तरह के संकेत को "फॉरवर्ड रेफरेंस" ( [1] , [2] ) कहा जाता है, और पीईपी 563 पायथन के साथ 3.7+ में __future__आयात होने वाला है (डिफ़ॉल्ट रूप से 4.0 में सक्षम होना चाहिए) जो आगे के संदर्भों का उपयोग करने की अनुमति देगा उद्धरण के बिना, उनके मूल्यांकन को स्थगित करना।

* AFAICT रनटाइम के दौरान केवल स्थानीय चर एनोटेशन का मूल्यांकन नहीं किया जाता है। (स्रोत: पीईपी 526 )


4
यह 3.6.1+ उपयोगकर्ताओं के लिए सबसे साफ समाधान की तरह लगता है। ध्यान दें कि यह उदाहरण खेतों के लिए प्रकार संकेत के रूप में भ्रमित करने वाला (थोड़ा) है leftऔर right(यानी Node) वर्ग के रूप में परिभाषित किया जा रहा है और इसलिए इसे स्ट्रिंग के रूप में लिखा जाना चाहिए।
101

1
@ 101, धन्यवाद, मैंने इस बारे में उत्तर में एक नोट जोड़ा है।
भिक्षु-समय

2
मुहावरे के लिए एनालॉग क्या है my_list: List[T] = None self.my_list = my_list if my_list is not None else []? क्या हम इस तरह से डिफ़ॉल्ट मापदंडों का उपयोग नहीं कर सकते हैं?
weberc2

@ weberc2 महान प्रश्न! मुझे यकीन नहीं है कि अगर यह परस्पर परिवर्तन के लिए समाधान है। मूल्यों के साथ संभव है typing.NamedTuple। लेकिन डेटा वर्गों के साथ आप Field एक default_factoryattr के साथ वस्तुओं का उपयोग कर सकते हैं । इसके लिए, अपने मुहावरे को बदलना my_list: List[T] = field(default_factory=list)
भिक्षु-समय

20

यह डॉक्स से सीधे एक उदाहरण है :

एक प्रोटोटाइप उदाहरण को अनुकूलित करने के लिए _replace () का उपयोग करके डिफ़ॉल्ट मानों को लागू किया जा सकता है:

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

तो, ओपी का उदाहरण होगा:

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

हालांकि, मुझे यहां दिए गए कुछ अन्य उत्तर अच्छे लगे। मैं इसे पूर्णता के लिए जोड़ना चाहता था।


2
+1। यह बहुत अजीब है कि उन्होंने एक _विधि के साथ जाने का फैसला किया (जो मूल रूप से एक निजी का मतलब है) कुछ के लिए replaceजो बहुत उपयोगी लगता है ..
sasuke

@ ससुके - मुझे भी आश्चर्य हो रहा था। यह पहले से ही थोड़ा अजीब है कि आप तत्वों को एक अलग स्ट्रिंग के स्थान पर परिभाषित करते हैं *args। यह सिर्फ इतना हो सकता है कि उन चीजों में से बहुत से मानकीकृत होने से पहले इसे भाषा में जोड़ा गया था।
टिम टिसडल

12
_उपसर्ग उपयोगकर्ता परिभाषित टपल क्षेत्रों के नाम के साथ टकराने से बचने के लिए है (प्रासंगिक दस्तावेज़ बोली: "कोई भी मान्य अजगर पहचानकर्ता एक अंडरस्कोर के साथ शुरू नामों के अलावा एक FIELDNAME के लिए इस्तेमाल किया जा सकता है।")। अंतरिक्ष के लिए अलग स्ट्रिंग के रूप में, मुझे लगता है कि यह बस कुछ कीस्ट्रोक्स को बचाने के लिए है (और यदि आप चाहें तो तार के अनुक्रम को पास कर सकते हैं)।
सोरेन लोर्बोर्ग

1
आह, हाँ, मैं भूल गया कि आप नामित टपल के तत्वों को गुणों के रूप में एक्सेस कर सकते हैं, इसलिए _बहुत समझ में आता है।
टिम टिसडल

2
आपका समाधान सरल और सबसे अच्छा है। बाकी आईएमएचओ बल्कि बदसूरत है। मैं केवल एक छोटा सा बदलाव करूंगा। Default_node के बजाय मैं node_default को प्राथमिकता दूंगा क्योंकि यह IntelliSense के साथ बेहतर अनुभव करता है। यदि आप नोड को टाइप करना शुरू करते हैं, तो आपको अपनी जरूरत का हर सामान मिला :)
Pavel Hanpari

19

मुझे यकीन नहीं है कि अगर वहाँ एक आसान तरीका है जिसमें बिल्ट-इन नेमटुपल है। एक अच्छा मॉड्यूल है जिसे रिकॉर्ड टाइप कहा जाता है जिसमें यह कार्यक्षमता है:

>>> 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
आह, तीसरे पक्ष के पैकेज का उपयोग करना संभव नहीं है, हालांकि recordtypeनिश्चित रूप से भविष्य के काम के लिए दिलचस्प लगता है। +1
sasuke

मॉड्यूल काफी छोटा है और केवल एक फ़ाइल है ताकि आप हमेशा इसे अपनी परियोजना में जोड़ सकें।
अपराह्न १२:४२ पर

उचित रूप से पर्याप्त है, हालांकि मैं शुद्ध नाम के कुछ और समय के लिए इंतजार करूंगा, यह स्वीकार किए जाने से पहले वहाँ एक है! :)
sasuke

सहमति शुद्ध अजगर अच्छा होगा, लेकिन मुझे नहीं लगता कि वहाँ एक :(
jterrace

3
बस ध्यान देने योग्य है कि नहीं है, recordtypeजबकि namedtupleहै। यदि आप चाहते हैं कि यह वस्तु हैज़िटेबल हो (जो मुझे लगता है कि आप नहीं चाहते हैं, क्योंकि यह एक वर्ग के रूप में शुरू हुई) यह बात हो सकती है।
बावजा

14

यहाँ एक और अधिक कॉम्पैक्ट संस्करण है, जो जस्टिनफे के उत्तर से प्रेरित है:

from collections import namedtuple
from functools import partial

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

7
खबरदार जो Node(1, 2)इस नुस्खा के साथ काम नहीं करता है, लेकिन @ justinfay के उत्तर में काम करता है। अन्यथा, यह काफी निफ्टी (+1) है।
जॉर्गेका

12

Python3.7 + में एक नया डिफॉल्ट = कीवर्ड तर्क है।

चूकNone डिफ़ॉल्ट मानों की पुनरावृत्ति हो सकती है । चूंकि डिफ़ॉल्ट मान वाले फ़ील्ड को डिफ़ॉल्ट के बिना किसी भी फ़ील्ड के बाद आना चाहिए, चूक को सबसे सही मापदंडों पर लागू किया जाता है। उदाहरण के लिए, यदि फ़ील्डनाम हैं ['x', 'y', 'z']और चूक हैं (1, 2), तो xएक आवश्यक तर्क yहोगा , डिफ़ॉल्ट होगा 1और zडिफ़ॉल्ट होगा 2

उदाहरण का उपयोग:

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

लघु, सरल, और लोगों को isinstanceअनुचित तरीके से उपयोग करने के लिए नेतृत्व नहीं करता है :

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

सभी लापता तर्कों को आरंभ करने के लिए थोड़ा विस्तारित उदाहरण 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

पायथॉन 3.7: defaultsनेमटपल परिभाषा में परम का परिचय ।

उदाहरण के रूप में प्रलेखन में दिखाया गया है:

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

और अधिक पढ़ें यहाँ


4

आप इसका उपयोग भी कर सकते हैं:

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)

यह मूल रूप से आपको डिफ़ॉल्ट मान के साथ किसी भी नामित टपल का निर्माण करने की संभावना देता है और उदाहरण के लिए आपके द्वारा आवश्यक मापदंडों को ओवरराइड करता है:

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

@ डिनीस और @ मर्क के दृष्टिकोण का मेल:

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) 

यह स्थिति संबंधी तर्कों के साथ और मिश्रित मामलों के साथ टपल बनाने का समर्थन करना चाहिए। परीक्षण के मामलों:

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

लेकिन यह भी TypeError का समर्थन:

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

3

मुझे यह संस्करण पढ़ने में आसान लगता है:

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)

यह उतना कुशल नहीं है, क्योंकि इसमें दो बार ऑब्जेक्ट के निर्माण की आवश्यकता होती है, लेकिन आप इसे बदल सकते हैं कि मॉड्यूल के अंदर डिफॉल्ट डंप को परिभाषित करके और फ़ंक्शन को रिप्लेस लाइन को करने के बाद।


3

चूंकि आप namedtupleएक डेटा क्लास के रूप में उपयोग कर रहे हैं , इसलिए आपको पता होना चाहिए कि अजगर 3.7 @dataclassइस उद्देश्य के लिए एक डेकोरेटर पेश करेगा - और निश्चित रूप से इसमें डिफ़ॉल्ट मान हैं।

डॉक्स से एक उदाहरण :

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

हैकिंग की तुलना में बहुत क्लीनर, पठनीय और प्रयोग करने योग्य namedtuple। यह अनुमान लगाना कठिन नहीं है कि namedtuples का उपयोग 3.7 को अपनाने के साथ घट जाएगा।


2

से प्रेरित इस जवाब एक अलग प्रश्न के लिए, यहाँ मेरे प्रस्तावित एक के आधार पर समाधान है metaclass और का उपयोग कर super(भविष्य सही ढंग से subcalssing संभाल करने के लिए)। यह जस्टिनफे के उत्तर के समान है ।

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

फिर:

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

रिकॉर्डटाइप का उपयोग करने के लिए jterrace का उत्तर बहुत अच्छा है, लेकिन लाइब्रेरी के लेखक ने अपनी नामसूची परियोजना का उपयोग करने की सिफारिश की है , जो परस्पर ( namedlist) और अपरिवर्तनीय ( 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

यहाँ डिफ़ॉल्ट तर्क के साथ नामांकित टुप के लिए एक अच्छा वाक्यविन्यास के साथ एक छोटा, सरल सामान्य उत्तर दिया गया है:

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

उपयोग:

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

न्यूनतम किया गया:

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

NamedTupleमेरी Advanced Enum (aenum)लाइब्रेरी से क्लास का उपयोग करना , और classसिंटैक्स का उपयोग करना , यह काफी सरल है:

from aenum import NamedTuple

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

एक संभावित दोष यह है कि __doc__एक डिफ़ॉल्ट मान के साथ किसी भी विशेषता के लिए एक स्ट्रिंग की आवश्यकता है (यह सरल विशेषताओं के लिए वैकल्पिक है)। उपयोग में यह ऐसा दिखता है:

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

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

इससे होने वाले फायदे 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)

सादगी है, साथ ही metaclassआधारित होने के बजाय execआधारित है।


0

एक और समाधान:

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

उपयोग:

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

यहां मार्क लोडेटो के रैपर का कम लचीला, लेकिन अधिक संक्षिप्त संस्करण है: यह एक शब्दकोश के रूप में फ़ील्ड और डिफॉल्ट को लेता है।

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

उदाहरण:

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
dictआदेश देने की कोई गारंटी नहीं है।
एतान फुरमान
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.