Python'da nesne nitelikleri üzerinde yineleme


162

Birkaç öznitelik ve yöntem ile bir python nesnesi var. Nesne öznitelikleri üzerinde yineleme yapmak istiyorum.

class my_python_obj(object):
    attr1='a'
    attr2='b'
    attr3='c'

    def method1(self, etc, etc):
        #Statements

Tüm nesne özniteliklerini ve geçerli değerlerini içeren bir sözlük oluşturmak istiyorum, ancak dinamik bir şekilde yapmak istiyorum (daha sonra başka bir öznitelik eklerseniz işlevimi de güncellemeyi hatırlamak zorunda değilim).

Php değişkenleri anahtar olarak kullanılabilir, ancak python nesneler açıklanamaz ve bunun için nokta gösterimi kullanırsanız, benim var adı olan yeni bir öznitelik oluşturur, bu benim niyetim değil.

Sadece işleri daha net hale getirmek için:

def to_dict(self):
    '''this is what I already have'''
    d={}
    d["attr1"]= self.attr1
    d["attr2"]= self.attr2
    d["attr3"]= self.attr3
    return d

·

def to_dict(self):
    '''this is what I want to do'''
    d={}
    for v in my_python_obj.attributes:
        d[v] = self.v
    return d

Güncelleme: Özniteliklerle yöntemlerin değil, yalnızca bu nesnenin değişkenleri anlamına gelir.


3
stackoverflow.com/questions/1251692/… Biraz yardımcı olabilir.
sean

@sean Özellikle, stackoverflow.com/a/1251789/4203 gidilecek yol; isteğe bağlı predicateargümanı, filtrelenen (bağlı?) işlevleri filtreleyecek (yöntemlerden kurtulan) bir çağrıyı iletmek için kullanırsınız .
Hank Gay

Yanıtlar:


227

Aşağıdaki gibi bir sınıfınız olduğunu varsayarsak:

>>> class Cls(object):
...     foo = 1
...     bar = 'hello'
...     def func(self):
...         return 'call me'
...
>>> obj = Cls()

dirnesneyi çağırdığınızda , bu nesnenin python özel öznitelikleri dahil tüm özniteliklerini geri verir. Yöntemler gibi bazı nesne nitelikleri çağrılabilir olsa da.

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo', 'func']

Özel yöntemleri her zaman bir liste kavrayışı kullanarak filtreleyebilirsiniz.

>>> [a for a in dir(obj) if not a.startswith('__')]
['bar', 'foo', 'func']

veya harita / filtreleri tercih ederseniz.

>>> filter(lambda a: not a.startswith('__'), dir(obj))
['bar', 'foo', 'func']

Yöntemleri filtrelemek istiyorsanız, yerleşik olanı callablebir kontrol olarak kullanabilirsiniz .

>>> [a for a in dir(obj) if not a.startswith('__') and not callable(getattr(obj, a))]
['bar', 'foo']

Kullanarak sınıfınız ve örnek nesnesi arasındaki farkı da inceleyebilirsiniz.

>>> set(dir(Cls)) - set(dir(object))
set(['__module__', 'bar', 'func', '__dict__', 'foo', '__weakref__'])

1
Bu kötü bir fikir, bunu tavsiye etmem bile, ama eğer yapacaksanız, en azından uyarıyı sağlayın.
Julian

2
@Meitham @Pablo Yanlış olan şey, esas olarak bunu yapmanız gerekmemesidir. İlgilendiğiniz özellikler, özel değil kapsayıcı olmalıdır . Daha önce gördüğünüz gibi, yöntemleri dahil edersiniz ve bunları hariç tutma girişimleri kusurlu olacaktır, çünkü veya gibi kötü şeyler içereceklerdir . Ara sonucu veya dahili veri yapısını saklayan "_foo" adlı bir özellik eklersem ne olur? Sınıfa zararsız bir şekilde bir öznitelik eklersem ne olur? callabletypes.MethodTypename
Julian

3
@Julian yukarıdaki kod her ikisini de seçecek _foove nameartık nesnenin nitelikleri oldukları için. Bunları dahil etmek istemiyorsanız, bunları liste anlama koşulunda hariç tutabilirsiniz. Yukarıdaki kod, yaygın bir python deyimi ve python nesnelerini iç içe geçirmenin popüler bir yoludur.
Meitham

30
Aslında obj .__ dict__ bu amaç için (muhtemelen) daha iyidir.
Dave

5
@Julian, metasınıflar dir()(bir uç kenar durumu) veya nitelikleri olan sınıflar @DynamicClassAttribute(başka bir uç kenar durumu) ile geçtiğinde "yatar" . Her iki durumda da, dirs()sarıcı çağırmak bunu inspect.getmembers()çözer. Ancak standart nesneler için, bu çözümün callables olmayanları filtreleyen liste anlama yaklaşımı kesinlikle yeterlidir. Bazı Pythonistaların bunu "kötü bir fikir" olarak nitelendirmesi beni şaşırttı, ama ... sanırım her birine kendi?
Cecil Curry

57

genel olarak sınıfınıza bir __iter__yöntem koyun ve nesne özniteliklerini yineleyin veya bu mixin sınıfını sınıfınıza koyun.

class IterMixin(object):
    def __iter__(self):
        for attr, value in self.__dict__.iteritems():
            yield attr, value

Senin sınıfın:

>>> class YourClass(IterMixin): pass
...
>>> yc = YourClass()
>>> yc.one = range(15)
>>> yc.two = 'test'
>>> dict(yc)
{'one': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 'two': 'test'}

bu yalnızca oneve twosonrasında tanımlandığında çalışır yc = YourClass(). Soru, içindeki mevcut geçişler arasında dolaşmayı soruyor YourClass(). Ayrıca, IterMixinsınıfı devralmak her zaman bir çözüm olarak mevcut olmayabilir.
Xiao

4
vars(yc)başka bir sınıftan miras almak zorunda kalmadan aynı sonucu verir.
Nathan

1
Yukarıdaki yorumum pek doğru değil; vars(yc)hemen hemen aynıdır, ancak size geri verdiği sözlük örneğin kendisidir __dict__, bu nedenle değiştirilmesi örneğe yansıtılacaktır. Bu, dikkatli değilseniz sorunlara yol açabilir, bu nedenle yukarıdaki yöntem bazen daha iyi olabilir (ancak sözlüğü kopyalamak muhtemelen daha kolaydır). Ayrıca, yukarıda döndürülen sözlük örneğin olmasa da __dict__, değerlerin aynı olduğunu unutmayın, bu nedenle değiştirilebilir değerleri değiştirmenin yine de örneğin özniteliklerine yansıtılacağını unutmayın.
Nathan

25

Zaten bazı cevaplarda / yorumlarda belirtildiği gibi, Python nesneleri zaten özniteliklerinin bir sözlüğünü saklar (yöntemler dahil değildir). Buna şu şekilde erişilebilir __dict__, ancak daha iyi bir yol kullanmaktır vars(yine de çıkış aynıdır). Bu sözlüğün değiştirilmesinin örnek üzerindeki nitelikleri değiştireceğini unutmayın! Bu yararlı olabilir, ancak aynı zamanda bu sözlüğü nasıl kullandığınız konusunda dikkatli olmanız gerektiği anlamına gelir. İşte kısa bir örnek:

class A():
    def __init__(self, x=3, y=2, z=5):
        self.x = x
        self._y = y
        self.__z__ = z

    def f(self):
        pass

a = A()
print(vars(a))
# {'x': 3, '_y': 2, '__z__': 5}
# all of the attributes of `a` but no methods!

# note how the dictionary is always up-to-date
a.x = 10
print(vars(a))
# {'x': 10, '_y': 2, '__z__': 5}

# modifying the dictionary modifies the instance attribute
vars(a)["_y"] = 20
print(vars(a))
# {'x': 10, '_y': 20, '__z__': 5}

Kullanmak dir(a), bu soruna açıkça kötü olmasa bile tuhaf bir yaklaşımdır. Sınıfın tüm niteliklerini ve yöntemlerini (örneğin özel yöntemler dahil) üzerinden yinelemeniz gerekiyorsa iyi olur __init__. Bununla birlikte, bu istediğiniz gibi görünmüyor ve kabul edilen cevap bile yöntemleri kaldırmak ve sadece nitelikleri bırakmak için biraz kırılgan filtreleme uygulayarak bu konuda zayıf gidiyor; bunun Ayukarıda tanımlanan sınıf için nasıl başarısız olacağını görebilirsiniz .

(kullanma __dict__birkaç cevapta yapılmıştır, ancak hepsi doğrudan kullanmak yerine gereksiz yöntemleri tanımlar. Yalnızca bir yorumun kullanılması önerilmektedir vars).


1
Vars () yaklaşımıyla çözemediğim bir şey, A sınıfının, kullanıcı tanımlı B sınıfı olan başka bir nesne olan bir üyesi olduğu durumun nasıl ele alınacağıdır. Vars (a) __repr __ () olarak adlandırılır. B türünün üyesinde. __repr __ (), anladığım gibi, bir dize döndürmesi gerekiyor. Ama vars (a)
dediğimde

1
Eğer varsa a.bbazı özel sınıf olarak Bdaha sonra vars(a)["b"] is a.b, sıra bir beklenebilir; başka hiçbir şey gerçekten mantıklı olmaz ( a.bsözdizimsel şeker olarak düşünün a.__dict__["b"]). Eğer varsa d = vars(a)ve çağırırsanız , sadece sınıf gerçekten bir dize olarak temsil edilmesi gerektiğini bilir gibi kendi repr-string döndürmenin bir parçası olarak repr(d)çağıracaktır . repr(d["b"])B
Nathan

2

Python'daki nesneler niteliklerini (işlevler dahil) çağrılan bir dikte içinde saklar __dict__. Niteliklere doğrudan erişmek için bunu kullanabilirsiniz (ancak genellikle kullanmamalısınız). Sadece bir liste dir(obj)istiyorsanız, daha sonra geçebileceğiniz tüm öznitelik adlarıyla bir yinelenebilir döndüren de arayabilirsiniz getattr.

Bununla birlikte, değişkenlerin isimleri ile herhangi bir şey yapmak genellikle kötü tasarımdır. Neden onları bir koleksiyonda tutmuyorsunuz?

class Foo(object):
    def __init__(self, **values):
        self.special_values = values

Daha sonra tuşlar üzerinde for key in obj.special_values:


Koleksiyonu istediğime ulaşmak için nasıl kullanacağımı biraz daha açıklayabilir misiniz?
Pablo Mescher

1
Nesneye dinamik olarak öznitelik eklemiyorum. Sahip olduğum değişkenler uzun süre aynı kalacak, ancak niyetim gibi bir şey yapabilmenin iyi olacağını düşünüyorum.
Pablo Mescher

1
class someclass:
        x=1
        y=2
        z=3
        def __init__(self):
           self.current_idx = 0
           self.items = ["x","y","z"]
        def next(self):
            if self.current_idx < len(self.items):
                self.current_idx += 1
                k = self.items[self.current_idx-1]
                return (k,getattr(self,k))
            else:
                raise StopIteration
        def __iter__(self):
           return self

o zaman bunu tekrarlanabilir olarak adlandırın

s=someclass()
for k,v in s:
    print k,"=",v

Bu çok karmaşık görünüyor. Başka seçeneğim yoksa, şu anda yaptığım şeye sadık kalmayı tercih ederim.
Pablo Mescher

0

Bunun doğru cevabı yapmamalısınız. Bu tür bir şey istiyorsanız ya sadece bir dikte kullanın, ya da bazı kapsayıcılara açıkça özellikler eklemeniz gerekir. Dekoratörleri öğrenerek bunu otomatikleştirebilirsiniz.

Özellikle, bu arada, örneğinizdeki method1 bir özellik kadar iyidir.


2
Evet, bilmemeliyim biliyorum ... ama işlerin nasıl yürüdüğünü anlamama yardımcı oluyor. Ben bir php arka plan geliyor ve günlük bu tür şeyler yapmak için kullanılır
Pablo Mescher

Beni ikna ettin. To_dict () yönteminin güncel tutulması, şimdiye kadar verilen yanıtlardan çok daha basittir.
Pablo Mescher

1
Dogmatik tavsiyeden kaçınacak kimse kaldı mı? Dikkatli değilseniz dinamik programlama sizi yakacak, ancak özellikler kullanılacak dilde.
Henrik Vendelbo

0

Python 3.6 için

class SomeClass:

    def attr_list(self, should_print=False):

        items = self.__dict__.items()
        if should_print:
            [print(f"attribute: {k}    value: {v}") for k, v in items]

        return items

6
Profesyonel olmayan pitonistlerin de cevaplarınızdan yararlanabilmesi için yayınlarınıza biraz açıklama ekleyebilir misiniz?
not2qubit

-3

Dışarıdaki tüm piton zealotları için Johan Cleeze'in dogmatizminizi onaylayacağından eminim;). Bu cevaptan ayrılıyorum ve onu daha da gizemli kılıyor. Tavuklar bir yorum bırakın!

Python 3.6 için

class SomeClass:

    def attr_list1(self, should_print=False):

        for k in self.__dict__.keys():
            v = self.__dict__.__getitem__(k)
            if should_print:
                print(f"attr: {k}    value: {v}")

    def attr_list(self, should_print=False):

        b = [(k, v) for k, v in self.__dict__.items()]
        if should_print:
            [print(f"attr: {a[0]}    value: {a[1]}") for a in b]
        return b

Bu soruya iki cevap vermenizin bir nedeni var mı?
Nathan

@Nathan var, biri daha ayrıntılı ve diğeri daha pythonian. Hangisinin daha hızlı çalıştığını da bilmiyordum, bu yüzden okuyucunun seçmesine izin verdim.
Lastik Ördek

@nathan piton ya da yığın taşması polisi misin? Farklı kodlama stilleri vardır. Pitonik olmayan birini tercih ederim çünkü birçok dilde ve teknolojide çalışıyorum ve bence kendine özgü bir eğilim. Bununla birlikte, Iist kavrayış serin ve özlüdür. Öyleyse neden aydınlanmış okuyucuyu göze alamıyorsunuz?
Lastik Ördek

Dışarıdaki tüm piton zealotları için, Johan Cleeze'in dogmatizminizi onaylayacağından eminim;). Bu cevaptan ayrılıyorum ve onu daha da gizemli kılıyor. Tavuklar bir yorum bırakın!
Rubber Duck
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.