एक पायथन नेमप्ले को जेन्स पर सीरियल करना


85

namedtupleफील्ड नामों को बनाए रखने के साथ ए टू जसन को क्रमबद्ध करने का अनुशंसित तरीका क्या है ?

जीन्स namedtupleको सीरीज़ करने से सिर्फ़ मूल्यों को क्रमबद्ध किया जा रहा है और क्षेत्र के नाम अनुवाद में खोते जा रहे हैं। मैं चाहूंगा कि जब जोस-ओइज्ड हो तो खेतों को भी बरकरार रखा जाए और इसलिए निम्नलिखित कार्य किए जाएं:

class foobar(namedtuple('f', 'foo, bar')):
    __slots__ = ()
    def __iter__(self):
        yield self._asdict()

उपरोक्त के रूप में मुझे उम्मीद है कि json के लिए क्रमबद्ध करता है और namedtupleअन्य स्थानों के रूप में व्यवहार करता है जिनका मैं उपयोग करता हूं (विशेषता पहुंच इत्यादि), एक गैर-टपल के अलावा, जैसे कि इसे पुनरावृत्त करते समय परिणाम (जो मेरे उपयोग के मामले में ठीक है)।

फील्ड नामों को बनाए रखने के साथ जोंस में परिवर्तित करने का "सही तरीका" क्या है?


पायथॉन 2.7 के लिए: stackoverflow.com/questions/16938456/…
लोटेक

जवाबों:


56

यह बहुत मुश्किल है, क्योंकि namedtuple()एक कारखाना है जो एक नए प्रकार से प्राप्त होता है tuple। एक दृष्टिकोण यह होगा कि आपकी कक्षा भी विरासत में मिली हो UserDict.DictMixin, लेकिन tuple.__getitem__पहले से ही एक पूर्णांक को परिभाषित करता है और यह अपेक्षा करता है कि तत्व की स्थिति को निरूपित किया जाए, न कि इसकी विशेषता का नाम:

>>> f = foobar('a', 1)
>>> f[0]
'a'

इसके दिल में नामित नाम JSON के लिए एक विषम फिट है, क्योंकि यह वास्तव में एक कस्टम-निर्मित प्रकार है जिसके प्रमुख नाम टाइप परिभाषा के हिस्से के रूप में तय किए गए हैं , एक शब्दकोश के विपरीत जहां प्रमुख नाम उदाहरण के अंदर संग्रहीत होते हैं। यह आपको नामांकित नाम के "राउंड-ट्रिपिंग" से रोकता है, उदाहरण के लिए, आप डिक्शनरी में किसी अन्य जानकारी के बिना किसी नाम-पत्र में वापस शब्दकोश में डिकोड नहीं कर सकते हैं, जैसे कि एक ऐप-विशिष्ट प्रकार का मार्कर {'a': 1, '#_type': 'foobar'}, जो कि थोड़ा सा हैकी है।

यह आदर्श नहीं है, लेकिन अगर आपको केवल नामांकितों को शब्दकोशों में सांकेतिक शब्दों में बदलना चाहिए , तो एक अन्य दृष्टिकोण इन प्रकारों को विशेष मामले में अपने JSON एनकोडर को विस्तारित या संशोधित करना है। यहाँ पायथन को उपवर्गित करने का एक उदाहरण दिया गया है json.JSONEncoder। यह सुनिश्चित करने की समस्या से निपटा जाता है कि नेस्टेड नल्टल्स को ठीक से शब्दकोशों में बदल दिया गया है:

from collections import namedtuple
from json import JSONEncoder

class MyEncoder(JSONEncoder):

    def _iterencode(self, obj, markers=None):
        if isinstance(obj, tuple) and hasattr(obj, '_asdict'):
            gen = self._iterencode_dict(obj._asdict(), markers)
        else:
            gen = JSONEncoder._iterencode(self, obj, markers)
        for chunk in gen:
            yield chunk

class foobar(namedtuple('f', 'foo, bar')):
    pass

enc = MyEncoder()
for obj in (foobar('a', 1), ('a', 1), {'outer': foobar('x', 'y')}):
    print enc.encode(obj)

{"foo": "a", "bar": 1}
["a", 1]
{"outer": {"foo": "x", "bar": "y"}}

12
इसके दिल में नामित नाम JSON के लिए एक विषम फिट है, क्योंकि यह वास्तव में एक कस्टम-निर्मित प्रकार है जिसके प्रमुख नाम टाइप परिभाषा के हिस्से के रूप में तय किए जाते हैं, एक शब्दकोश के विपरीत जहां प्रमुख नाम उदाहरण के अंदर संग्रहीत होते हैं। बहुत ही व्यावहारिक टिप्पणी। मैंने उसके बारे में नहीं सोचा था। धन्यवाद। मुझे नामित नाम पसंद हैं क्योंकि वे विशेषता नामकरण सुविधा के साथ एक अच्छी अपरिवर्तनीय संरचना प्रदान करते हैं । मैं आपका उत्तर स्वीकार करूंगा। यह कहने के बाद कि, जावा का क्रमिकीकरण तंत्र इस बात पर अधिक नियंत्रण प्रदान करता है कि वस्तु को किस प्रकार अनुक्रमित किया जाता है और मैं यह जानने के लिए उत्सुक हूं कि पायथन में इस तरह के हुक क्यों नहीं लगते हैं।
कैल्विनक्रिशी

यह मेरा पहला दृष्टिकोण था, लेकिन यह वास्तव में (मेरे लिए) काम नहीं करता है।
zeekay

1
>>> json.dumps(foobar('x', 'y'), cls=MyEncoder) <<< '["x", "y"]'
ज़ेकेय

19
आह, अजगर 2.7+ _iterencode में JSONEncoder की विधि नहीं है।
zeekay

2
@calvin धन्यवाद, मुझे यह नामांकित भी उपयोगी लगता है, काश यह JSON के लिए पुनरावर्ती रूप से सांकेतिक शब्दों में बदलना करने के लिए एक बेहतर समाधान था। @ ज़ेकेय यप, 2.7+ में लगता है कि वे इसे छिपाते हैं, इसलिए इसे अब ओवरराइड नहीं किया जा सकता। यह निराशाजनक है।
सैंपलबीज

77

यदि यह सिर्फ एक है namedtupleजिसे आप क्रमबद्ध करना चाहते हैं, तो इसकी _asdict()विधि का उपयोग करके काम करेंगे (पायथन के साथ = = 2.7)

>>> from collections import namedtuple
>>> import json
>>> FB = namedtuple("FB", ("foo", "bar"))
>>> fb = FB(123, 456)
>>> json.dumps(fb._asdict())
'{"foo": 123, "bar": 456}'

4
मैं AttributeError हो रही है: 'अमेरिकन प्लान' ऑब्जेक्ट कोई विशेषता 'है dict ' जब अजगर 2.7 (64) में है कि कोड चलाने विंडोज पर। हालाँकि fb._asdict () ठीक काम करता है।
geographika

5
fb._asdict()या vars(fb)बेहतर होगा।
jpmc26

1
@ jpmc26: आप varsबिना किसी ऑब्जेक्ट पर उपयोग नहीं कर सकते __dict__
रफ्लेविंड

@ रफ़लविंड आप __dict__उन पर उपयोग नहीं कर सकते हैं, या तो। =)
jpmc26

4
अजगर में 3 __dict__को हटा दिया गया है। _asdictदोनों पर काम करता प्रतीत होता है।
एंडी हेडन

21

ऐसा लगता है कि आप simplejson.JSONEncoderइस काम को करने के लिए उपवर्ग का उपयोग करने में सक्षम थे , लेकिन नवीनतम सिम्पसन कोड के साथ, अब ऐसा नहीं है: आपको वास्तव में परियोजना कोड को संशोधित करना होगा। मुझे लगता है कि कोई कारण नहीं है कि सिम्पलसन को नामितों का समर्थन नहीं करना चाहिए, इसलिए मैंने परियोजना को कांटा, नामांकित समर्थन को जोड़ा, और मैं वर्तमान में अपनी शाखा को मुख्य परियोजना में वापस खींचने का इंतजार कर रहा हूं । यदि आपको अब सुधार की आवश्यकता है, तो बस मेरे कांटे से खींचें।

संपादित करें : ऐसा लगता है कि नवीनतम संस्करण simplejsonअब मूल रूप से इस namedtuple_as_objectविकल्प का समर्थन करते हैं , जो कि चूक करता है True


3
आपका संपादन सही उत्तर है। सिंपलसन नामांकितों के नाम से अलग तरीके से क्रमबद्ध करता है (मेरी राय: बेहतर)। यह वास्तव में पैटर्न बनाता है: "ट्राय करें: सिंपलजसन को जसन के रूप में आयात करें: सिवाय जासन के आयात करें", जोखिम भरा है क्योंकि आप कुछ मशीनों पर अलग-अलग व्यवहार प्राप्त कर सकते हैं यदि सिम्पसन को स्थापित किया गया है। इस कारण से, मुझे अब अपनी बहुत सी सेटअप फाइलों में सिम्पलसन की आवश्यकता है और उस पैटर्न से परहेज करना है।
15:75 बजे mar7575

1
@ marr75 - डिट्टो के लिए ujson, जो इस तरह के किनारे के मामलों में और भी अधिक विचित्र और अप्रत्याशित है ...
मैक

मैं एक पुनरावर्ती नामांकित नाम प्राप्त करने में सक्षम था (सुंदर-मुद्रित) जसन का उपयोग करके:simplejson.dumps(my_tuple, indent=4)
KFL

5

मैंने ऐसा करने के लिए एक पुस्तकालय लिखा: https://github.com/ltworf/typedload

यह से और नाम-ट्यूपल और वापस जा सकते हैं।

यह सूची, सेट, एनम, यूनियनों, डिफ़ॉल्ट मूल्यों के साथ काफी जटिल नेस्टेड संरचनाओं का समर्थन करता है। इसमें अधिकांश सामान्य मामलों को शामिल किया जाना चाहिए।

संपादित करें: लाइब्रेरी डेटाक्लास और एटर कक्षाओं का भी समर्थन करती है।


2

यह पुन: नामांकित डेटा को पुन: json में कनवर्ट करता है।

print(m1)
## Message(id=2, agent=Agent(id=1, first_name='asd', last_name='asd', mail='2@mai.com'), customer=Customer(id=1, first_name='asd', last_name='asd', mail='2@mai.com', phone_number=123123), type='image', content='text', media_url='h.com', la=123123, ls=4512313)

def reqursive_to_json(obj):
    _json = {}

    if isinstance(obj, tuple):
        datas = obj._asdict()
        for data in datas:
            if isinstance(datas[data], tuple):
                _json[data] = (reqursive_to_json(datas[data]))
            else:
                 print(datas[data])
                _json[data] = (datas[data])
    return _json

data = reqursive_to_json(m1)
print(data)
{'agent': {'first_name': 'asd',
'last_name': 'asd',
'mail': '2@mai.com',
'id': 1},
'content': 'text',
'customer': {'first_name': 'asd',
'last_name': 'asd',
'mail': '2@mai.com',
'phone_number': 123123,
'id': 1},
'id': 2,
'la': 123123,
'ls': 4512313,
'media_url': 'h.com',
'type': 'image'}

1
+1 मैंने लगभग समान बनाया। लेकिन आपकी वापसी एक तानाशाह नहीं है। आपके पास "नहीं 'होना चाहिए, और यदि आपकी वस्तु का एक मूल्य एक बूलियन है, तो इसे सच में परिवर्तित नहीं किया जाएगा। मुझे लगता है कि यह तानाशाही में बदलने के लिए सुरक्षित है, तो json.dumps का उपयोग करके json में परिवर्तित करें।
फ्रेड लॉरेंट

2

डेकोरेटर का उपयोग करने के लिए एक अधिक सुविधाजनक समाधान है (यह संरक्षित क्षेत्र का उपयोग करता है _fields)।

पायथन 2.7+:

import json
from collections import namedtuple, OrderedDict

def json_serializable(cls):
    def as_dict(self):
        yield OrderedDict(
            (name, value) for name, value in zip(
                self._fields,
                iter(super(cls, self).__iter__())))
    cls.__iter__ = as_dict
    return cls

#Usage:

C = json_serializable(namedtuple('C', 'a b c'))
print json.dumps(C('abc', True, 3.14))

# or

@json_serializable
class D(namedtuple('D', 'a b c')):
    pass

print json.dumps(D('abc', True, 3.14))

अजगर 3.6.6+:

import json
from typing import TupleName

def json_serializable(cls):
    def as_dict(self):
        yield {name: value for name, value in zip(
            self._fields,
            iter(super(cls, self).__iter__()))}
    cls.__iter__ = as_dict
    return cls

# Usage:

@json_serializable
class C(NamedTuple):
    a: str
    b: bool
    c: float

print(json.dumps(C('abc', True, 3.14))

ऐसा मत करो, वे हर समय आंतरिक एपीआई बदलते हैं। मेरे टाइप किए गए लायब्रेरी में अलग-अलग पाई संस्करणों के लिए कई मामले हैं।
लेटफॉर्फ़

हाँ, यह स्पष्ट है। हालांकि, किसी को भी परीक्षण के बिना एक नए पायथन संस्करण में माइग्रेट नहीं करना चाहिए। और, अन्य समाधान उपयोग करते हैं _asdict, जो एक "संरक्षित" वर्ग सदस्य भी है।
दिमित्री टी।

1
LtWorf, आपकी लाइब्रेरी GPL है और फ्रोज़ेन्सेट्स के साथ काम नहीं करता है
थॉमस

2
@LtWorf आपका पुस्तकालय भी उपयोग करता है _fields;-) github.com/ltworf/typedload/blob/master/typedload/datadumper.py यह namedtuple के सार्वजनिक एपीआई का हिस्सा है, वास्तव में: docs.python.org/3.7/library/... लोग से उलझन में हो अंडरस्कोर (कोई आश्चर्य नहीं!)। यह खराब डिजाइन है, लेकिन मुझे नहीं पता कि उनके पास और क्या विकल्प था।
quant_dev 13

1
क्या बातें? कब? क्या आप जारी नोटों का हवाला दे सकते हैं?
मात्रा_देव

2

Jsonplus पुस्तकालय NamedTuple उदाहरण के लिए एक serializer प्रदान करता है। यदि आवश्यक हो तो साधारण वस्तुओं को आउटपुट करने के लिए इसकी संगतता मोड का उपयोग करें, लेकिन डिफ़ॉल्ट को प्राथमिकता दें क्योंकि यह डिकोडिंग के लिए सहायक है।


मैंने यहां अन्य समाधानों को देखा और बस इस निर्भरता को जोड़ने पर मुझे बहुत समय बचा लिया। विशेष रूप से क्योंकि मेरे पास NamedTuples की एक सूची थी जिसे मुझे सत्र में json के रूप में पारित करने की आवश्यकता थी। jsonplus से आप मूल रूप से tuples के नाम को json से बाहर .dumps()और .loads()उसके साथ जोड़ सकते हैं और यह काम नहीं करता है।
रॉब

1

देशी अजगर अजगर पुस्तकालय के साथ सही ढंग से नामांकितों को अनुक्रमित करना असंभव है। यह हमेशा सूचियों के रूप में ट्यूपल्स को देखेगा, और इस व्यवहार को बदलने के लिए डिफ़ॉल्ट धारावाहिक को ओवरराइड करना असंभव है। यदि वस्तुओं को नेस्टेड किया जाता है तो यह बदतर है।

बेहतर की तरह एक और अधिक मजबूत पुस्तकालय का उपयोग करने के orjson :

import orjson
from typing import NamedTuple

class Rectangle(NamedTuple):
    width: int
    height: int

def default(obj):
    if hasattr(obj, '_asdict'):
        return obj._asdict()

rectangle = Rectangle(width=10, height=20)
print(orjson.dumps(rectangle, default=default))

=>

{
    "width":10,
    "height":20
}

1
मैं orjsonभी एक प्रशंसक हूँ ।
CircleOnCircles

0

यह एक पुराना सवाल है। तथापि:

एक ही प्रश्न वाले सभी लोगों के लिए एक सुझाव, किसी भी निजी या आंतरिक विशेषताओं का उपयोग करने के बारे में सावधानी से सोचें NamedTupleक्योंकि उनके पास पहले है और समय के साथ फिर से बदल जाएगा।

उदाहरण के लिए, यदि आपकी NamedTupleएक फ्लैट वैल्यू ऑब्जेक्ट है और आप इसे केवल सीरियल करने में रुचि रखते हैं, न कि उन मामलों में, जहां इसे किसी अन्य ऑब्जेक्ट में नेस्टेड किया जाता है, तो आप उन परेशानियों से बच सकते हैं जो __dict__हटाए जाने या _as_dict()बदलने के साथ आएंगी और कुछ ऐसा ही करेंगी (और हाँ यह पायथन 3 है क्योंकि यह उत्तर वर्तमान के लिए है):

from typing import NamedTuple

class ApiListRequest(NamedTuple):
  group: str="default"
  filter: str="*"

  def to_dict(self):
    return {
      'group': self.group,
      'filter': self.filter,
    }

  def to_json(self):
    return json.dumps(self.to_dict())

यदि उपलब्ध हो तो कॉल करने के लिए defaultमैंने कॉल करने योग्य क्वार्ग का उपयोग करने की कोशिश की , लेकिन वह नहीं मिला जैसा कि एक सूची में परिवर्तनीय है।dumpsto_dict()NamedTuple


3
_asdictनामांकित सार्वजनिक API का हिस्सा है। वे अंडरस्कोर docs.python.org/3.7/library/… " का कारण बताते हैं, टुपल्स से विरासत में मिली विधियों के अलावा, टुपल्स तीन अतिरिक्त विधियों और दो विशेषताओं का समर्थन करते हैं। फ़ील्ड नामों, विधि और विशेषता नामों के साथ टकराव को रोकने के लिए। एक अंडरस्कोर के साथ शुरुआत करें। ”
क्वांट_देव

@quant_dev धन्यवाद, मैंने वह स्पष्टीकरण नहीं देखा। यह एपीआई स्थिरता की गारंटी नहीं है, लेकिन यह उन तरीकों को अधिक भरोसेमंद बनाने में मदद करता है। मैं स्पष्ट रूप से पढ़ने की योग्यता को पसंद करता हूं, लेकिन मैं देख सकता हूं कि यह _as_dict
dlamblin

0

यहाँ समस्या पर मेरा लेना है। यह NamedTuple को क्रमबद्ध करता है, उनके अंदर मुड़े हुए NamedTuples और Lists का ध्यान रखता है

def recursive_to_dict(obj: Any) -> dict:
_dict = {}

if isinstance(obj, tuple):
    node = obj._asdict()
    for item in node:
        if isinstance(node[item], list): # Process as a list
            _dict[item] = [recursive_to_dict(x) for x in (node[item])]
        elif getattr(node[item], "_asdict", False): # Process as a NamedTuple
            _dict[item] = recursive_to_dict(node[item])
        else: # Process as a regular element
            _dict[item] = (node[item])
return _dict

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.