Bir nesnenin alanlarından Python sözlüğü


344

Rasgele bir nesneden sözlük oluşturmak için yerleşik bir işlev olup olmadığını biliyor musunuz? Böyle bir şey yapmak istiyorum:

>>> class Foo:
...     bar = 'hello'
...     baz = 'world'
...
>>> f = Foo()
>>> props(f)
{ 'bar' : 'hello', 'baz' : 'world' }

NOT: Yöntem içermemelidir. Sadece alanlar.

Yanıtlar:


422

Python 2.7'deki en iyi uygulamanın yeni stil sınıfları (Python 3 ile gerekli değildir) kullanmak olduğunu unutmayın.

class Foo(object):
   ...

Ayrıca, bir 'nesne' ile 'sınıf' arasında bir fark vardır. Rasgele bir nesneden sözlük oluşturmak için kullanmak yeterlidir __dict__. Genellikle, yöntemlerinizi sınıf düzeyinde ve niteliklerinizi örnek düzeyinde beyan edersiniz, bu yüzden iyi __dict__olmalıdır. Örneğin:

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

Daha iyi bir yaklaşım ( yorumlarda robert tarafından önerilen ) yerleşik varsişlevdir:

>>> vars(a)
{'c': 2, 'b': 1}

Alternatif olarak, ne yapmak istediğinize bağlı olarak, miras almak güzel olabilir dict. Daha sonra sınıfınız zaten bir sözlüktür ve isterseniz sözlüğü geçersiz kılabilir getattrve / veya setattrarayabilir ve ayarlayabilirsiniz. Örneğin:

class Foo(dict):
    def __init__(self):
        pass
    def __getattr__(self, attr):
        return self[attr]

    # etc...

3
A'nın özelliklerinden birinin özel alıcısı varsa ne olur? (@property dekoratörü olan bir işlev)? Hala ____dict____ içinde görünüyor mu? Değeri ne olacak?
zakdances

11
__dict__nesne yuvalar kullanıyorsa (veya bir C modülünde tanımlanmışsa) çalışmaz.
Antimon

1
Sınıf nesneleri için bu yöntemin eşdeğeri var mı? IE f = Foo () kullanmak ve sonra f .__ dict__ yapmak yerine, doğrudan Foo .__ dict__ mi?
chiffa

49
Üzgünüm, buna geç geliyorum ama yapmamalıyım vars(a)? Benim için __dict__doğrudan çağırmak tercih edilir .
robert

2
ikinci örnek için __getattr__ = dict.__getitem__, davranışı tam olarak çoğaltmak daha iyi olur , o zaman aynı zamanda __setattr__ = dict.__setitem__ve __delattr__ = dict.__delitem__tamlık için de istersiniz .
Tadhg McDonald-Jensen

141

Bunun yerine, x.__dict__aslında kullanmak daha pitoniktir vars(x).


3
Kabul. MyClass(**my_dict)Sınıf özniteliklerini yansıtan parametrelere sahip bir yapıcı tanımladığınızı varsayarak , diğer yolu (dict-> class) yazarak da dönüştürebileceğinizi unutmayın . Özel özelliklere erişmeye veya dikteyi geçersiz kılmaya gerek yok.
tvt173

2
Neden daha Pitonik olduğunu açıklayabilir misiniz?
Hugh W

1
İlk olarak, Python genellikle dunder öğelerini doğrudan çağırır ve dolaylı olarak erişmek için neredeyse her zaman bir yöntem veya işlev (veya operatör) vardır. Genel olarak, dunder öznitelikleri ve yöntemleri bir uygulama detayıdır ve "sarmalayıcı" işlevini kullanarak ikisini ayırmanıza izin verir. İkincisi, bu şekilde varsişlevi geçersiz kılabilir ve nesnenin kendisini değiştirmeden ek işlevsellik getirebilirsiniz .
Berislav Lopac

1
Sınıfınız yine de kullanıyorsa yine de başarısız olur __slots__.
cz

Bu doğru ve ben her zaman uzatmak varsiçin iyi bir yön olduğunu hissettim , yani __dict__"yarıklı" sınıfları için eşdeğer bir dönmek için. Şimdilik, __dict__geri dönen bir özellik ekleyerek taklit edilebilir {x: getattr(self, x) for x in self.__slots__}(bunun performansı veya davranışı herhangi bir şekilde etkileyip etkilemediğinden emin değilsiniz).
Berislav Lopac

59

dirBuiltin size gibi özel yöntemler dahil tüm nesnenin özelliklerini, verecek __str__, __dict__ve muhtemelen istemiyorum başkalarının bir sürü. Ama şöyle bir şey yapabilirsiniz:

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

Bu nedenle, propsişlevinizi şu şekilde tanımlayarak yöntemleri değil veri özelliklerini döndürmek için genişletebilirsiniz :

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

1
Bu kod yöntemleri içerir. Yöntemleri hariç tutmanın bir yolu var mı? Sadece nesnenin alanlarına ihtiyacım var. Teşekkürler
Julio César

ismethodfonksiyonları yakalamaz. Örnek: inspect.ismethod(str.upper). inspect.isfunctionYine de çok daha yararlı değil. Buna nasıl yaklaşacağınızdan emin değilim.
Ehtesh Choudhury

Kabaca yinelenen ve tüm hataları burada derinlemesine görmezden gelmek için bazı değişiklikler yaptım, teşekkürler! gist.github.com/thorsummoner/bf0142fd24974a0ced778768a33a3069
ThorSummoner

26

Her iki cevabın bir kombinasyonu ile karar verdim:

dict((key, value) for key, value in f.__dict__.iteritems() 
    if not callable(value) and not key.startswith('__'))

Bu da işe yarıyor, ancak sınıfta değil, örneğin örnekte ayarlanan nitelikleri vereceğini unutmayın (örneğinizdeki Foo sınıfı gibi) ...
dF.

Yani, jcarrascal, yukarıdaki kodu props () gibi bir işleve sarmak daha iyidir, o zaman pro (f) veya props (Foo) diyebilirsiniz. 'Satır içi' kod yazmak yerine, neredeyse her zaman bir işlev yazmaktan daha iyi olduğunuzu unutmayın.
quamrana

Güzel, btw not sadece python2.7 için, sadece öğeleri () ile python3 relpace iteritems ().
Morten

Peki ya staticmethod? Öyle değil callable.
Alex

17

Dikte etmek için bir nesneyi nasıl çevirebileceğinizi size göstermek için biraz zaman alacağımı düşündüm 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'}"

Bu kodun anahtar kısmı __iter__işlevdir.

Yorumların açıkladığı gibi, yaptığımız ilk şey Sınıf öğelerini almak ve '__' ile başlayan her şeyi önlemek.

Bunu oluşturduktan dictsonra, updatedict işlevini kullanabilir ve örneği iletebilirsiniz __dict__.

Bunlar üyelerin tam bir sınıf + örnek sözlüğünü verecektir. Şimdi geriye kalan tek şey onları tekrarlamak ve getirileri sağlamak.

Ayrıca, bunu çok kullanmayı planlıyorsanız, bir @iterablesınıf dekoratörü oluşturabilirsiniz .

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

Bu, tüm yöntemleri de kapsıyor, ancak yalnızca sınıf + örnek alanlarına ihtiyacımız var. Belki dict((x, y) for x, y in KpiRow.__dict__.items() if x[:2] != '__' and not callable(y))çözecek? Ama yine de staticyöntemler olabilir :(
Alex

15

Rasgele bir nesneden sözlük oluşturmak için kullanmak yeterlidir __dict__.

Bu, nesnenin sınıfından miras aldığı nitelikleri özlüyor. Örneğin,

class c(object):
    x = 3
a = c()

hasattr (a, 'x') doğru, ancak 'x' bir dict__'de görünmüyor


Bu durumda çözüm nedir? Çünkü vars()işe yaramıyor
19:19

@should_be_working dirbu durumda çözümdür. Bununla ilgili diğer cevaba bakınız.
Albert

8

Geç cevap, ancak Google çalışanlarının eksiksizliği ve yararları için sağlanmıştır:

def props(x):
    return dict((key, getattr(x, key)) for key in dir(x) if key not in dir(x.__class__))

Bu, sınıfta tanımlanan yöntemleri göstermeyecektir, ancak yine de lambdaslara atananlar veya çift alt çizgiyle başlayanlar da dahil olmak üzere alanları gösterecektir.


6

Bence en kolay yol sınıf için bir getitem niteliği oluşturmak . Nesneye yazmanız gerekiyorsa, özel bir setattr oluşturabilirsiniz . İşte getitem için bir örnek :

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 sözlüğe nesnelerin niteliklerini oluşturur ve sözlük nesnesi ihtiyacınız olan öğeyi elde etmek için kullanılabilir.


Bu cevaptan sonra hala bir nesneden nasıl sözlük alınacağı açık değildir. Özellikler değil, tüm sözlük;)
maxkoryukov

6

Kullanmanın bir dezavantajı __dict__sığ olmasıdır; alt sınıfları sözlüklere dönüştürmez.

Python3.5 veya üstünü kullanıyorsanız şunları kullanabilirsiniz jsons:

>>> import jsons
>>> jsons.dump(f)
{'bar': 'hello', 'baz': 'world'}

3

Özelliklerinizin bir kısmını listelemek istiyorsanız, geçersiz kılın __dict__:

def __dict__(self):
    d = {
    'attr_1' : self.attr_1,
    ...
    }
    return d

# Call __dict__
d = instance.__dict__()

instanceBazı büyük blok verileriniz varsa ve dmesaj kuyruğu gibi Redis'e zorlamak istiyorsanız, bu çok yardımcı olur .


__dict__bir özniteliktir, bir yöntem değildir, bu nedenle bu örnek arabirimi değiştirir (yani, çağrılabilir olarak çağırmanız gerekir), bu yüzden geçersiz kılmaz.
Berislav Lopac

0

PYTHON 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

Şimdi yukarıdaki kodu kendi sınıfınızda kullanabilirsiniz:

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

0

vars() harika, ancak nesnelerin iç içe nesneleri için çalışmıyor

Nesnelerin iç içe geçmiş nesnesini dikte etmeye dönüştürün:

def to_dict(self):
    return json.loads(json.dumps(self, default=lambda o: o.__dict__))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.