قاموس بايثون من حقول الكائن
-
09-06-2019 - |
سؤال
هل تعرف ما إذا كانت هناك وظيفة مضمنة لبناء قاموس من كائن عشوائي؟أود أن أفعل شيئًا كهذا:
>>> class Foo:
... bar = 'hello'
... baz = 'world'
...
>>> f = Foo()
>>> props(f)
{ 'bar' : 'hello', 'baz' : 'world' }
ملحوظة: لا ينبغي أن تشمل الأساليب.الحقول فقط.
المحلول
لاحظ أن أفضل الممارسات في Python 2.7 هي الاستخدام أسلوب جديد الفئات (غير مطلوبة مع Python 3)، على سبيل المثال.
class Foo(object):
...
هناك أيضًا فرق بين "الكائن" و"الفئة".لبناء قاموس من التعسفي هدف, ، يكفي للاستخدام __dict__
.عادةً، ستعلن عن أساليبك على مستوى الفصل وسماتك على مستوى المثيل، لذلك __dict__
يجب ان يكون بخير.على سبيل المثال:
>>> class A(object):
... def __init__(self):
... self.b = 1
... self.c = 2
... def do_nothing(self):
... pass
...
>>> a = A()
>>> a.__dict__
{'c': 2, 'b': 1}
نهج أفضل (اقترحه روبرت في التعليقات) هو المدمج vars
وظيفة:
>>> vars(a)
{'c': 2, 'b': 1}
وبدلاً من ذلك، اعتمادًا على ما تريد القيام به، قد يكون من الجيد أن ترث منه dict
.ثم فصلك هو بالفعل قاموس، وإذا كنت تريد يمكنك تجاوز getattr
و/أو setattr
للاتصال من خلال وتعيين الإملاء.على سبيل المثال:
class Foo(dict):
def __init__(self):
pass
def __getattr__(self, attr):
return self[attr]
# etc...
نصائح أخرى
بدلاً من x.__dict__
, ، إنها في الواقع أكثر استخدامًا بيثونية vars(x)
.
ال dir
المدمج في سوف يعطيك كافة سمات الكائن، بما في ذلك أساليب خاصة مثل __str__
, __dict__
ومجموعة كاملة من الآخرين الذين ربما لا تريدهم.ولكن يمكنك القيام بشيء مثل:
>>> class Foo(object):
... bar = 'hello'
... baz = 'world'
...
>>> f = Foo()
>>> [name for name in dir(f) if not name.startswith('__')]
[ 'bar', 'baz' ]
>>> dict((name, getattr(f, name)) for name in dir(f) if not name.startswith('__'))
{ 'bar': 'hello', 'baz': 'world' }
لذلك يمكن توسيع هذا ليعود فقط سمات البيانات وليس الأساليب، من خلال تحديد الخاص بك props
وظيفة مثل هذا:
import inspect
def props(obj):
pr = {}
for name in dir(obj):
value = getattr(obj, name)
if not name.startswith('__') and not inspect.ismethod(value):
pr[name] = value
return pr
لقد استقرت على مزيج من الإجابتين:
dict((key, value) for key, value in f.__dict__.iteritems()
if not callable(value) and not key.startswith('__'))
لبناء قاموس من التعسفي هدف, ، يكفي للاستخدام
__dict__
.
وهذا يفتقد السمات التي يرثها الكائن من فئته.على سبيل المثال،
class c(object):
x = 3
a = c()
hasattr(a, 'x') صحيح، لكن 'x' لا يظهر في a.__dict__
اعتقدت أنني سأستغرق بعض الوقت لأوضح لك كيف يمكنك ترجمة كائن للإملاء من خلاله dict(obj)
.
class A(object):
d = '4'
e = '5'
f = '6'
def __init__(self):
self.a = '1'
self.b = '2'
self.c = '3'
def __iter__(self):
# first start by grabbing the Class items
iters = dict((x,y) for x,y in A.__dict__.items() if x[:2] != '__')
# then update the class items with the instance items
iters.update(self.__dict__)
# now 'yield' through the items
for x,y in iters.items():
yield x,y
a = A()
print(dict(a))
# prints "{'a': '1', 'c': '3', 'b': '2', 'e': '5', 'd': '4', 'f': '6'}"
القسم الرئيسي من هذا الرمز هو __iter__
وظيفة.
كما توضح التعليقات، أول شيء نقوم به هو الاستيلاء على عناصر الفصل الدراسي ومنع أي شيء يبدأ بـ "__".
بمجرد إنشاء ذلك dict
, ، ثم يمكنك استخدام update
وظيفة dict وتمرير في المثيل __dict__
.
سيعطيك هذا قاموسًا كاملاً للفصل + مثيل للأعضاء.الآن كل ما تبقى هو التكرار عليها والحصول على العائدات.
أيضًا، إذا كنت تخطط لاستخدام هذا كثيرًا، فيمكنك إنشاء ملف @iterable
مصمم الطبقة.
def iterable(cls):
def iterfn(self):
iters = dict((x,y) for x,y in cls.__dict__.items() if x[:2] != '__')
iters.update(self.__dict__)
for x,y in iters.items():
yield x,y
cls.__iter__ = iterfn
return cls
@iterable
class B(object):
d = 'd'
e = 'e'
f = 'f'
def __init__(self):
self.a = 'a'
self.b = 'b'
self.c = 'c'
b = B()
print(dict(b))
إجابة متأخرة ولكن مع توفير الاكتمال والاستفادة من مستخدمي Google:
def props(x):
return dict((key, getattr(x, key)) for key in dir(x) if key not in dir(x.__class__))
لن يُظهر هذا الأساليب المحددة في الفصل الدراسي، لكنه سيظل يُظهر الحقول بما في ذلك تلك المخصصة لـ lambdas أو تلك التي تبدأ بشرطة سفلية مزدوجة.
أعتقد أن أسهل طريقة هي إنشاء ملف تحصل على البند السمة للفئة.إذا كنت بحاجة إلى الكتابة إلى الكائن، يمكنك إنشاء مخصص setattr .هنا مثال ل تحصل على البند:
class A(object):
def __init__(self):
self.b = 1
self.c = 2
def __getitem__(self, item):
return self.__dict__[item]
# Usage:
a = A()
a.__getitem__('b') # Outputs 1
a.__dict__ # Outputs {'c': 2, 'b': 1}
vars(a) # Outputs {'c': 2, 'b': 1}
قاموس ينشئ سمات الكائنات في القاموس ويمكن استخدام كائن القاموس للحصول على العنصر الذي تحتاجه.
إذا كنت تريد إدراج جزء من سماتك، فقم بتجاوزها __dict__
:
def __dict__(self):
d = {
'attr_1' : self.attr_1,
...
}
return d
# Call __dict__
d = instance.__dict__()
وهذا يساعد كثيرا إذا كان لديك instance
احصل على بعض بيانات الكتلة الكبيرة وتريد الدفع d
إلى Redis مثل قائمة انتظار الرسائل.
الجانب السلبي للاستخدام __dict__
هو أنها ضحلة.لن يقوم بتحويل أي فئات فرعية إلى قواميس.
إذا كنت تستخدم Python3.5 أو أعلى، فيمكنك استخدام jsons
:
>>> import jsons
>>> jsons.dump(f)
{'bar': 'hello', 'baz': 'world'}
بايثون 3:
class DateTimeDecoder(json.JSONDecoder):
def __init__(self, *args, **kargs):
JSONDecoder.__init__(self, object_hook=self.dict_to_object,
*args, **kargs)
def dict_to_object(self, d):
if '__type__' not in d:
return d
type = d.pop('__type__')
try:
dateobj = datetime(**d)
return dateobj
except:
d['__type__'] = type
return d
def json_default_format(value):
try:
if isinstance(value, datetime):
return {
'__type__': 'datetime',
'year': value.year,
'month': value.month,
'day': value.day,
'hour': value.hour,
'minute': value.minute,
'second': value.second,
'microsecond': value.microsecond,
}
if isinstance(value, decimal.Decimal):
return float(value)
if isinstance(value, Enum):
return value.name
else:
return vars(value)
except Exception as e:
raise ValueError
يمكنك الآن استخدام الكود أعلاه داخل فصلك الدراسي:
class Foo():
def toJSON(self):
return json.loads(
json.dumps(self, sort_keys=True, indent=4, separators=(',', ': '), default=json_default_format), cls=DateTimeDecoder)
Foo().toJSON()