"Nesne" sınıfının örneğinde öznitelikler ayarlanamaz


88

Bu yüzden, bu soruyu cevaplarken Python ile oynuyordum ve bunun geçerli olmadığını keşfettim:

o = object()
o.attr = 'hello'

nedeniyle bir AttributeError: 'object' object has no attribute 'attr'. Bununla birlikte, nesneden miras alınan herhangi bir sınıf için geçerlidir:

class Sub(object):
    pass

s = Sub()
s.attr = 'hello'

Yazdırma s.attr, beklendiği gibi 'merhaba' görüntüler. Bu neden böyle? Python dil spesifikasyonunda vanilya nesnelerine nitelik atayamayacağınızı belirtir?


Saf varsayım: objectTür değişmez ve yeni nitelikler eklenemez mi? Bu en mantıklı olacak gibi görünüyor.
Chris Lutz

2
@ S.Lott: Bu sorunun ilk satırına bakın. Tamamen merak.
Smashery

3
Başlığınız yanıltıcı, nitelikleri objectsınıfta değil sınıf örneklerinde belirlemeye çalışıyorsunuz object.
dhill

Yanıtlar:


130

Rasgele öznitelik atamasını desteklemek için, bir nesnenin bir __dict__: nesneye ilişkin bir dikteye ihtiyacı vardır , burada rasgele öznitelikler depolanabilir. Aksi takdirde, yeni nitelikler koyacak yer yoktur .

Bir örneği objectyok değil taşımak bir __dict__- bu yaptıysam, korkunç dairesel bağımlılık sorunu (öncesinden bu yana dict, başka pek çok şey gibi, devralır object, bu altına sokacak ;-) her bir yükü anlamına gelecektir ki, bir dict ile Python nesne ait birçok şu anda sahip veya dicti ihtiyacı yoktur o nesnenin başına bayt (aslında, yok tüm nesneler keyfi atanabilir nitelikler varsa veya bir dicti gerekmez).

Örneğin, mükemmel pymplerprojeyi kullanarak ( buradan svn aracılığıyla alabilirsiniz ), bazı ölçümler yapabiliriz ...:

>>> from pympler import asizeof
>>> asizeof.asizeof({})
144
>>> asizeof.asizeof(23)
16

Her intbirinin yalnızca 16 yerine 144 baytı almasını istemezsiniz , değil mi? -)

Şimdi, bir sınıf oluşturduğunuzda (her neyse), işler değişir ...:

>>> class dint(int): pass
... 
>>> asizeof.asizeof(dint(23))
184

... __dict__ olduğu şimdi eklenen (artı, biraz daha havai) - Bir nedenle dintörneği keyfi nitelikleri olabilir, ancak bu esneklik için oldukça uzay maliyetini ödemek.

Peki ya intsadece bir ekstra özelliğe sahip olmak isteseydin foobar...? Bu nadir bir ihtiyaç, ancak Python bu amaç için özel bir mekanizma sunuyor ...

>>> class fint(int):
...   __slots__ = 'foobar',
...   def __init__(self, x): self.foobar=x+100
... 
>>> asizeof.asizeof(fint(23))
80

... değil oldukça bir şekilde minik olarak int, çekerim! (veya hatta iki ints, biri selfve biri self.foobar- ikincisi yeniden atanabilir), ama kesinlikle a'dan çok daha iyi dint.

Sınıf sahip olduğunda __slots__özel niteliği (dizeleri dizisi), sonra classdeyimi (daha doğrusu varsayılan metaclass, typeetmez) değil bir ile bu sınıfın her örneğini donatmak __dict__(keyfi özelliklere sahiptir ve bu nedenle yeteneği), sadece sonlu , verilen isimlerle katı "yuvalar" (temelde her biri bir nesneye bir referans tutabilen yerler).

Kayıp esneklik karşılığında, örnek başına çok fazla bayt kazanırsınız (muhtemelen yalnızca etrafta dolaşan zilyonlarca örneğiniz varsa anlamlıdır, ancak bunun için kullanım durumları vardır).


5
Bu, mekanizmanın nasıl uygulandığını açıklar, ancak neden bu şekilde uygulandığını açıklamaz. Anında dikte eklemenin en az iki veya üç yolunu düşünebilirim; bu, genel giderleri olumsuz etkilemeyecek, ancak biraz basitlik katacak.
Георги Кременлиев

Boş olmayan geldiğini hatırlatırız __slots__gibi değişken uzunlukta türleriyle işi yok str, tupleve ayrıca Python 3'te int.
arekolek


Bu harika bir açıklamadır, ancak yine Subde __dict__özniteliğin neden (veya nasıl) olmadığını ve nesnenin Submiras aldığını object, bu öznitelik (ve benzerleri __module__) kalıtıma nasıl eklenir? Bu yeni bir soru olabilir mi
Rodrigo

2
Bir nesne __dict__yalnızca ihtiyaç duyulduğunda oluşturulur, bu nedenle bellek maliyeti durumu, asizeofçıktının görünmesini sağladığı kadar basit değildir . ( materyalizasyondan asizeofnasıl kaçınılacağını bilmiyor __dict__.) Bu örnekte , diktenin ihtiyaç duyulana kadar gerçekleşmediğini görebilir ve __dict__materyalizasyondan sorumlu kod yollarından birini burada görebilirsiniz .
user2357112, Monica'yı

17

Diğer yanıt verenlerin de söylediği gibi, bir'nin a'ya sahip objectolmadığı __dict__. veya dahil tüm türlerin objecttemel sınıfıdır . Böylelikle sağlanan her şey onlara da yük olacaktır. İsteğe bağlı kadar basit bir şey bile her değer için fazladan bir işaretçiye ihtiyaç duyar; bu, çok sınırlı bir yardımcı program için sistemdeki her nesne için ek 4-8 ​​bayt bellek israfına neden olur.intstrobject __dict__


Python 3.3+ sürümünde sahte bir sınıf örneği yapmak yerine, bunun için kullanabilirsiniz (ve kullanmalısınız) types.SimpleNamespace.


4

Bu sadece optimizasyondan kaynaklanmaktadır.

Dicts nispeten büyüktür.

>>> import sys
>>> sys.getsizeof((lambda:1).__dict__)
140

C'de tanımlanan çoğu (belki tüm) sınıfların optimizasyon için bir diktesi yoktur.

Kaynak koduna bakarsanız , nesnenin bir dikteye sahip olup olmadığını görmek için birçok kontrol olduğunu göreceksiniz.


1

Bu yüzden, kendi sorumu araştırırken, Python dili hakkında şunu keşfettim: int gibi şeylerden miras alabilirsin ve aynı davranışı görüyorsun:

>>> class MyInt(int):
       pass

>>> x = MyInt()
>>> print x
0
>>> x.hello = 4
>>> print x.hello
4
>>> x = x + 1
>>> print x
1
>>> print x.hello
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: 'int' object has no attribute 'hello'

Sondaki hatanın add işlevinin bir int döndürmesinden kaynaklandığını varsayıyorum, bu nedenle __add__özel özniteliklerimi korumak için ve benzeri işlevleri geçersiz kılmam gerekir . Ama bunların hepsi şimdi bana mantıklı geliyor (sanırım), "nesne" yi "int" gibi düşündüğümde.


0

Bunun nedeni, nesnenin bir sınıf değil, bir "tip" olmasıdır. Genel olarak, C uzantılarında tanımlanan tüm sınıflar (tüm yerleşik veri türleri ve numpy dizileri gibi şeyler gibi), rastgele özniteliklerin eklenmesine izin vermez.


Ancak object () bir nesnedir, Sub () gibi bir nesne. Anladığım kadarıyla hem s hem de o nesnelerdir. Peki s ve o arasındaki temel fark nedir? Biri somutlaştırılmış tür, diğeri somutlaştırılmış bir sınıf mı?
Smashery

Bingo. Sorun da bu.
Ryan

1
Python 3'te türler ve sınıflar arasındaki fark gerçekte yoktur. Yani "tip" ve "sınıf" artık oldukça eşanlamlıdır. Ancak __dict__, Alex Martelli'nin verdiği nedenlerden dolayı, a'ya sahip olmayan sınıflara nitelikler ekleyemezsiniz .
PM 2Ring


-2

Bu (IMO) Python'daki temel sınırlamalardan biridir - sınıfları yeniden açamazsınız. Yine de asıl sorunun, C'de uygulanan sınıfların çalışma zamanında değiştirilememesinden kaynaklandığına inanıyorum ... alt sınıflar olabilir, ancak temel sınıflar değiştirilemez.

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.