2009-12-10 13 views
8

Chciałem stworzyć wyrzucany obiekt "struct", aby zachować różne flagi stanu. Moje pierwsze podejście było to (javascript styl)Obiekty strukturalne w pythonie

>>> status = object() 
>>> status.foo = 3 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'object' object has no attribute 'foo' 
nie

Zdecydowanie czego się spodziewałem, bo to działa:

>>> class Anon: pass 
... 
>>> b=Anon() 
>>> b.foo = 4 

Chyba to dlatego, że obiekt() nie posiada __dict__. Nie chcę używać słownika i zakładając, że nie chcę tworzyć obiektu Anon, czy istnieje inne rozwiązanie?

+2

Zobacz http://stackoverflow.com/questions/1264833/python-class-factory-to-produce-simple-struct-like-classes –

+0

Używam Pythona 2.5, i tak czy inaczej, nazwa krotki nie pozwalają, o ile rozumiem, na dynamiczne podłączanie nowych członków. Z podanych przykładów nie wynika jasno. –

+0

@Stefano Borini: więc w zasadzie prosisz o dyktando, gdzie możesz wymówić 'foo.bar' zamiast' foo ['bar'] '? –

Odpowiedz

21

najbardziej zwięzły sposób, aby „rodzajowy obiekt, do którego można przypisać/pobrać atrybutów” to prawdopodobnie:

b = lambda:0 

jak większość innych odpowiedzi podkreślić, istnieje wiele innych sposobów, ale trudno pokonaj tę dla zwięzłości (lambda:0 jest dokładnie taka sama liczba znaków jak object() ... ;-).

+20

Tylko jeśli nie policzymy znaków, które wyjaśniają, do czego to służy! : D –

+0

woah! : D +1000 dla Alexa –

+1

@Stefano, dzięki! @gnibbler, lambda jest wystarczająco mistyczna, że ​​nie musisz wyjaśniać, po prostu robisz tajemnicze gesty palców w powietrzu i wszyscy cofają się w strachu (opcjonalnie, mroczny szept "lambda, the Ultimate ...!", http: // lambda-the-ultimate.org/ i/lub zobacz http://en.wikipedia.org/wiki/Lambda dla wielu innych niejasnych odniesień). –

3

Spróbuj

>>> status=type('status',(object,),{})() 
>>> status.foo=3 
>>> status.foo 
3 

Nie trzeba dać nazwę klasy A, jeśli nie chcą

>>> status=type('',(object,),{})() 
>>> status.__class__.__name__ 
'' 
+0

to jest ładne i hacky :) –

+0

Przepraszam gnibbler, odpowiedź jest dobra, ale Alex "jeden zostawił mnie zdumiony :) –

+0

czy nikt nie widział żart tutaj? jest taki sam jak "status klasy (obiekt): pass". –

15

Z Python Official Documentation:

9.7. Drobiazgi

Czasami dobrze jest mieć dane typ podobny do „record” Pascal lub C „struktury”, łączenie ze sobą kilku nazwanych elementów danych. Pusta klasa definicja zrobi ładnie:

class Employee: 
    pass 

john = Employee() # Create an empty employee record 

# Fill the fields of the record 
john.name = 'John Doe' 
john.dept = 'computer lab' 
john.salary = 1000 

To wydaje się naturalne i proste: pythonic. Remember the Zen! „Proste jest lepsze niż złożone” (nr 3) oraz „Jeśli realizacja jest łatwe do wyjaśnienia, to może być dobry pomysł” (numer 11)

Ponadto struct jest niczym class z członków publicznych (tj. struct{}; i class{public:}; to dokładnie to samo (na przykład C++)). Czy nie powinieneś tego rozważać i unikać sztucznych konstrukcji w swoim programie Python? Python powinien być czytelny, obsługiwany i łatwy do zrozumienia.

+3

Ugh, to stara dokumentacja i powinni ją wyczyścić. Ten przykładowy kod tworzy "klasę starego stylu". Nie jest to już zalecane, a w Pythonie 3.x nawet nie działa. Aby zrobić dokładnie ten przykład jako klasa nowy styl, który będzie działał w Pythonie 3.x: 'klasa pracownika (Object): pass' To wszystko, co musisz zrobić, po prostu odziedziczyć od' object' i masz klasę w nowym stylu. – steveha

+1

To jest dokładnie tak jak w przykładzie z Anon, który OP powiedział, że nie chce tego robić. –

+0

Tak, to dobrze. Zirytowało go to, że musisz zdefiniować status klasy: pass, a następnie status = status(). –

2

Tajemnica tutaj jest różnica między obiektów i klasowych przypadkach.

W Pythonie wszystko jest przedmiotem. Klasy są obiektami, liczbami całkowitymi są obiekty, typy są obiektami, a instancje klasy są obiektami. Kiedy mówisz: object(), otrzymujesz zwykły obiekt na poziomie podstawowym. To nic. Kompletnie bezużyteczny. Niższy poziom niż cokolwiek innego, co możesz odwołać w Pythonie.

Prawdopodobnie uważasz, że wywołanie object() daje instancję klasy. Co jest zrozumiałe, ponieważ prawdopodobnie uważasz, że object jest klasą. To nie jest. Nawet jeśli myślisz tak, ponieważ jest to „klasa” zasada stosowana do definicji klas w nowym stylu jak:

class MyClass(object): 
    pass 

object jest w rzeczywistości typ (jak jak str i int są rodzaje).Kiedy dzwonisz pod numer object(), nie tworzysz instancji klasy, która tworzy instancję specjalnego typu obiektu. Ale w przypadku object jest to wyjątkowe pod względem tego, jak bardzo jest to blah.

Tylko instancje klas mają specjalną zdolność do poprawiania działania za pomocą notacji kropkowej. To nie jest ogólna właściwość wszystkich obiektów. Wyobraź sobie, że tak było! Możesz robić szalone rzeczy, takie jak dodawanie właściwości do ciągów:

s = "cat" 
s.language = "english" 

Oczywiście nie możesz tego zrobić.

+3

'object()' zwraca instancję. Czasami jest używany, jeśli potrzebujesz unikatowej wartości. –

8

Raz miałem to samo pytanie. Zapytałem o to na liście mailingowej, a Alex Martelli wskazał, że object jest podstawą całego dziedziczenia w Pythonie; jeśli object() utworzył instancję klasy z własnym słownikiem, to każdy obiekt w Pythonie musiałby mieć własny słownik, a to by marnowało pamięć. Na przykład: True i False są obiektami; najwyraźniej nie potrzebują oni własnych słowników!

Byłbym szczęśliwy, gdyby to był jakiś rodzaj wbudowaną funkcją Pythona, gdzie mogę tylko powiedzieć:

x = struct() 
x.foo = 1 
x.bar = 2 

Ale to jest trywialne napisać struct():

class struct(object): 
    pass 

Albo mógłbyś Zrobić nieco bardziej skomplikowany:

class struct(object): 
    def __init__(self, **kwargs): 
     self.__dict__.update(kwargs) 

Bardziej złożony jeden pozwala jest:

x = struct(foo=1, bar=2) 
print(x.foo) # prints 1 
print(x.bar) # prints 2 
x.baz = 3 
print(x.baz) # prints 3 

Ale to jest tak trywialne napisać struct() że myślę, że nie został uznany za wart dodając do języka. Może powinniśmy naciskać, aby dodać standardową funkcję do modułu collections lub coś podobnego.

+0

Aby zachęcić do myślenia pytonowego i czytelnego kodu, możesz użyć 'struct'. 'Struct' jest wyraźniej" klasą ", a nie wbudowaną lub funkcją. Ale potrafię docenić twoją myśl, że tworzysz nowy, osobisty "wbudowany" bardzo podobny do wbudowanej klasy 'object'. I małe litery 'struct' po prostu wygląda prościej. – hobs

+0

Właściwie to zazwyczaj nazywam tę banalną klasę 'Box()', gdy piszę ją w moich programach. Tutaj zaproponowałem wbudowane, a wbudowane w Python małe litery (jak 'deque'), z wyjątkiem sytuacji, gdy nie są (jak' Counter'). Ja wolę kapitalizować nazwy klas, ale chyba nie będę edytować odpowiedzi. – steveha

+0

@steveha Obudowa wbudowanych w Pythona oznacza język, w którym są zaimplementowane. Jeśli są zaimplementowane w języku C, wszystkie są pisane małymi literami, ale jeśli są zaimplementowane w języku Python, są klasami ClassCase. Zobacz tę odpowiedź (http://stackoverflow.com/a/14974045/1490091), aby uzyskać więcej informacji. –

4

Osobiście uważam, że najczystsze rozwiązaniem jest to, co już zgadliście:

class Scratch(object): 
    pass 

s = Scratch() 
s.whatever = 'you want' 

Wiem, że nie chcesz się __dict__, ale myli mnie jak nie widzę powodu, aby Dbaj o to. Nie musisz odwoływać się do __dict__, to jest wewnętrzny szczegół implementacji Pythona. W każdym razie każda instancja w Pythonie, która pozwala na dynamiczne dodawanie atrybutów, będzie miała wartość __dict__, ponieważ w ten sposób Python wykonuje atrybuty dynamiczne. Nawet jeśli instancja zostanie utworzona w naprawdę sprytny sposób, będzie miała __dict__.

Jeśli jeszcze tego nie zrobiłeś, polecam lekturę PEP 20 i PEP 8 (brak reputacji, więc tylko jeden link dla mnie.) Nie znaczy to, że PEP odnoszą się bezpośrednio do twojego pytania, ale myślę, że jest to użyteczne w rozpoczęciu używania Język Python w stylu Pythonic.

2

Oczywiście zawsze jest namedtuple. Jest tani i szybki, ale niezmienny - musisz znać wcześniej swoje nazwy i wartości atrybutów, a później nie możesz zmienić ich wartości. Ale przynajmniej ma on atrybuty struct/object attribute. I to będzie pracować z Python 2 i 3

>>> from collections import namedtuple 
>>> Status = namedtuple('Status', 'a b') 
>>> s = Status(a=1, b=2) 
>>> s.a + s.b 
3 
1

Jeśli chcesz semantyka obiektu JavaScript, można rozważyć użycie namespaces library. Najpierw musisz tylko pip install namespaces.

>>> import namespaces as ns 
>>> b = ns.Namespace() 
>>> b.foo = 4 
>>> b 
Namespace(foo=4) 

pełne oświadczenie: Jestem autorem biblioteki namespaces.

Powiązane problemy