Bir sınıf veya sözlük kullanmalı mıyım?


105

Yalnızca alanlar içeren ve bunun gibi yöntem içermeyen bir sınıfım var:

class Request(object):

    def __init__(self, environ):
        self.environ = environ
        self.request_method = environ.get('REQUEST_METHOD', None)
        self.url_scheme = environ.get('wsgi.url_scheme', None)
        self.request_uri = wsgiref.util.request_uri(environ)
        self.path = environ.get('PATH_INFO', None)
        # ...

Bu kolayca bir dikteye çevrilebilir. Sınıf, gelecekteki eklemeler için daha esnektir ve hızlı olabilir __slots__. Öyleyse bunun yerine bir dikt kullanmanın bir faydası olur mu? Bir dikt, bir sınıftan daha mı hızlı olur? Ve slotlu bir sınıftan daha hızlı mı?


2
Veri tutmak için her zaman sözlükler kullanıyorum ve bu bana bir kullanım durumu gibi görünüyor. bazı durumlarda 'den bir sınıf türetmek dictmantıklı olabilir. temiz avantaj: hata ayıkladığınızda, sadece söyleyin print(request)ve tüm durum bilgilerini kolayca göreceksiniz. daha klasik yaklaşımla, özel __str__yöntemlerinizi yazmanız gerekecek, bu da her zaman yapmak zorunda kalırsanız berbattır.
akış

Bunlar birbiriyle değiştirilebilir stackoverflow.com/questions/1305532/…
Oleksiy

Sınıf tamamen mantıklıysa ve diğerleri için açıksa, neden olmasın? Ayrıca, örneğin ortak arabirimlere sahip birçok sınıf tanımlarsanız, neden olmasın? Ancak Python, C ++ gibi güçlü nesne yönelimli kavramları desteklemez.
MasterControlProgram

4
@Ralf python hangi OOP'yi desteklemiyor?
qwr

Yanıtlar:


34

Bunu neden sözlük yapıyorsun? Avantajı nedir? Daha sonra bir kod eklemek istersen ne olur? Nerede olacağını __init__kod gitti?

Sınıflar, ilgili verileri (ve genellikle kodu) bir araya getirmek içindir.

Sözlükler, genellikle anahtarların hepsinin aynı türde olduğu ve tüm değerlerin de tek bir türde olduğu anahtar-değer ilişkilerini depolamak içindir. Anahtar / öznitelik adlarının tamamı önceden bilinmediğinde, bazen verileri gruplamak için yararlı olabilirler, ancak bu genellikle tasarımınızda bir sorun olduğuna işaret eder.

Bunu bir sınıf olarak tutun.


Sınıf __init__yöntemi yerine bir dikt oluşturan bir tür fabrika yöntemi yaratırdım . Ama haklısın: Birbirine ait olan şeylerden ayrılırdım.
deamon

89
Size daha fazla katılamazdım: sözlükler, kümeler, listeler ve demetler hepsi ilgili verileri bir araya getirmek için var. tam tersine, bir sözlüğün değerlerinin aynı veri türünde olması veya olması gerektiğine dair hiçbir varsayım yoktur. Birçok liste ve kümede değerler aynı türdedir, ancak bunun nedeni çoğunlukla birlikte numaralandırmaktan hoşlandığımız şeydir. Aslında, verileri tutmak için sınıfların yaygın olarak kullanılmasının oop'un kötüye kullanılması olduğunu düşünüyorum; serileştirme sorunları hakkında düşündüğünüzde nedenini kolayca görebilirsiniz.
akış

4
NESNE odaklı programlamadır ve bir nedenle sınıf odaklı değildir: nesnelerle ilgileniriz. Bir nesne 2 (3) özellik ile karakterize edilir: 1. durum (üyeler) 2. davranış (yöntemler) ve 3. örnek birkaç kelimeyle tanımlanabilir. Bu nedenle sınıflar, durum ve davranışı bir araya getirmek içindir.
friendzis

14
Bunu işaretledim çünkü her zaman mümkün olduğunda daha basit veri yapısını varsayılan olarak kullanmalısınız. Bu durumda, amaçlanan amaç için bir sözlük yeterlidir. Soru where would your __init__ code go?ilgiliydi. Daha az deneyimli bir geliştiriciyi, sözlükte bir init yöntemi kullanılmadığı için yalnızca sınıfların kullanılması gerektiğine ikna edebilir . Absürt.
Lloyd Moore

1
@Ralf Foo sınıfı, int ve string gibi yalnızca bir türdür. Değeri bir tam sayı türünde mi yoksa tam sayı türündeki değişken foo'da mı depoluyorsunuz? İnce ama önemli anlamsal fark. C gibi dillerde bu ayrım akademik çevrelerin dışında pek alakalı değildir. Çoğu OO dilinin sınıf değişkeni desteğine sahip olmasına rağmen, bu da sınıf ve nesne / örnek arasındaki farkı son derece alakalı hale getirir - verileri sınıfta mı (tüm örnekler arasında paylaşılan) veya belirli bir nesnede mi depoluyorsunuz? Do not get ısırıldı
friendzis

44

Bir sınıfın ekstra mekanizmasına ihtiyacınız yoksa bir sözlük kullanın. Ayrıca namedtuplehibrit bir yaklaşım için de kullanabilirsiniz :

>>> from collections import namedtuple
>>> request = namedtuple("Request", "environ request_method url_scheme")
>>> request
<class '__main__.Request'>
>>> request.environ = "foo"
>>> request.environ
'foo'

Buradaki performans farklılıkları minimum düzeyde olacak, ancak sözlük daha hızlı olmasa şaşırırdım.


15
"Buradaki performans farklılıkları minimum düzeyde olacak , ancak sözlük önemli ölçüde daha hızlı olmasa şaşırırdım." Bu hesaplanmaz. :)
mipadi

1
@mipadi: bu doğru. Şimdi düzeltildi: p
Katriel

Dikte, adlandırılmış başlığa göre 1,5 kat ve yuvasız bir sınıfa göre iki kat daha hızlıdır. Bu cevapla ilgili yazımı kontrol edin.
alexpinho98

@ alexpinho98: Bahsettiğiniz 'gönderiyi' bulmak için çok uğraştım, ama bulamıyorum! URL'yi sağlayabilir misin? Teşekkürler!
Dan Oblinger

@DanOblinger Aşağıda cevabını kastettiğini varsayıyorum.
Adam Lewis

37

Python A sınıfı olan dict altında. Sınıf davranışıyla biraz ek yük alırsınız, ancak profil oluşturucu olmadan bunu fark edemezsiniz. Bu durumda, dersten yararlandığınıza inanıyorum çünkü:

  • Tüm mantığınız tek bir işlevde yaşar
  • Güncellemesi kolaydır ve kapsüllenmiş halde kalır
  • Daha sonra herhangi bir şeyi değiştirirseniz, arayüzü kolayca aynı tutabilirsiniz

Tüm mantığınız tek bir işlevde yaşamıyor. Bir sınıf, paylaşılan bir durum demeti ve genellikle bir veya daha fazla yöntemdir. Bir sınıfı değiştirirseniz, arayüzü hakkında size herhangi bir garanti verilmez.
Lloyd Moore

26

Bence her birinin kullanımı, benim buna giremeyecek kadar özneldir, bu yüzden sadece sayılara bağlı kalacağım.

Bir diktede bir değişken oluşturmak ve değiştirmek için gereken zamanı, bir new_style sınıfı ve slotlu bir new_style sınıfını karşılaştırdım.

İşte test etmek için kullandığım kod (biraz dağınık ama işi yapıyor.)

import timeit

class Foo(object):

    def __init__(self):

        self.foo1 = 'test'
        self.foo2 = 'test'
        self.foo3 = 'test'

def create_dict():

    foo_dict = {}
    foo_dict['foo1'] = 'test'
    foo_dict['foo2'] = 'test'
    foo_dict['foo3'] = 'test'

    return foo_dict

class Bar(object):
    __slots__ = ['foo1', 'foo2', 'foo3']

    def __init__(self):

        self.foo1 = 'test'
        self.foo2 = 'test'
        self.foo3 = 'test'

tmit = timeit.timeit

print 'Creating...\n'
print 'Dict: ' + str(tmit('create_dict()', 'from __main__ import create_dict'))
print 'Class: ' + str(tmit('Foo()', 'from __main__ import Foo'))
print 'Class with slots: ' + str(tmit('Bar()', 'from __main__ import Bar'))

print '\nChanging a variable...\n'

print 'Dict: ' + str((tmit('create_dict()[\'foo3\'] = "Changed"', 'from __main__ import create_dict') - tmit('create_dict()', 'from __main__ import create_dict')))
print 'Class: ' + str((tmit('Foo().foo3 = "Changed"', 'from __main__ import Foo') - tmit('Foo()', 'from __main__ import Foo')))
print 'Class with slots: ' + str((tmit('Bar().foo3 = "Changed"', 'from __main__ import Bar') - tmit('Bar()', 'from __main__ import Bar')))

Ve işte çıktı ...

Oluşturuluyor ...

Dict: 0.817466186345
Class: 1.60829183597
Class_with_slots: 1.28776730003

Bir değişkeni değiştirme ...

Dict: 0.0735140918748
Class: 0.111714198313
Class_with_slots: 0.10618612142

Yani, sadece değişkenleri depoluyorsanız, hıza ihtiyacınız var ve çok fazla hesaplama yapmanızı gerektirmiyorsa, bir dikt kullanmanızı tavsiye ederim (her zaman sadece bir metoda benzeyen bir fonksiyon yapabilirsiniz). Ancak, gerçekten derslere ihtiyacınız varsa, unutmayın - her zaman __ yuva __ kullanın .

Not:

"Sınıf" ı hem new_style hem de old_style sınıflarıyla test ettim . Eski stil sınıflarının oluşturulması daha hızlı ancak değiştirilmesinin daha yavaş olduğu ortaya çıkıyor (sıkı bir döngüde çok sayıda sınıf oluşturuyorsanız önemli değil ama önemli (ipucu: yanlış yapıyorsunuz)).

Ayrıca benimki eski ve yavaş olduğu için, değişken oluşturma ve değiştirme zamanları bilgisayarınızda farklılık gösterebilir. 'Gerçek' sonuçları görmek için kendiniz test ettiğinizden emin olun.

Düzenle:

Daha sonra adlandırılmış grubu test ettim: onu değiştiremiyorum ama 10000 örnek (veya bunun gibi bir şey) oluşturmak için 1.4 saniye sürdü, bu yüzden sözlük gerçekten en hızlısı.

İ Eğer dict işlevini değiştirmek bunu oluştururken anahtarlarını ve değerlerini içerecek şekilde ve dicti içeren değişken yerine dicti dönmek için bana verir 0,65 yerine 0,8 saniye.

class Foo(dict):
    pass

Yaratmak, yuvaları olan bir sınıfa benzer ve değişkeni değiştirmek en yavaş olanıdır (0.17 saniye), bu nedenle bu sınıfları kullanmayın . bir dikte (hız) veya nesneden türetilen sınıf ('sözdizimi şekeri') için gidin


Bir alt sınıf için sayıları görmek istiyorum dict(geçersiz kılınan yöntemler olmadan, sanırım?). Sıfırdan yazılmış yeni tarz bir sınıfla aynı şekilde mi işliyor?
Benjamin Hodgson

13

@Adw'ye katılıyorum. Bir sözlükle asla bir "nesneyi" (OO anlamında) temsil etmem. Sözlükler ad / değer çiftlerini toplar. Sınıflar nesneleri temsil eder. Nesnelerin sözlüklerle temsil edildiği kodlar gördüm ve nesnenin gerçek şeklinin ne olduğu belirsiz. Belirli adlar / değerler olmadığında ne olur? Müşterinin herhangi bir şey koymasını engelleyen şey. Veya herhangi bir şey çıkarmaya çalışması. Bir şeyin şekli her zaman açıkça tanımlanmalıdır.

Python kullanırken, dil, yazarın kendisini ayağından vurması için birçok yola izin verdiği için disiplinle inşa etmek önemlidir.


9
SO'daki cevaplar oylara göre sıralanır ve aynı oy sayısına sahip cevaplar rastgele sıralanır. Bu yüzden lütfen "son afiş" ile kimi kastettiğinizi açıklayın.
Mike DeSimone

4
Ve sadece alanları olan ve işlevselliği olmayan bir şeyin OO anlamında bir "nesne" olduğunu nasıl anlarsınız?
Muhammad Alkarouri

1
Turnusol testim "bu verilerin yapısı sabit mi?" Cevabınız evet ise, o zaman bir nesne kullanın, aksi takdirde bir dikte. Bundan ayrılmak sadece kafa karışıklığı yaratacaktır.
weberc2

Çözdüğünüz problemin içeriğini analiz etmelisiniz. Bu manzaraya aşina olduğunuzda nesneler görece açık olmalıdır. Şahsen, geri döndüğünüz şey ad / değer çiftlerinin bir eşlemesi olmadığı sürece, son çare olarak bir sözlüğü iade etmenin olması gerektiğini düşünüyorum. Yöntemlere giren ve çıkan her şeyin sadece bir sözlük olduğu çok fazla özensiz kod gördüm. Tembel. Dinamik olarak yazılmış dilleri seviyorum ama aynı zamanda kodumun açık ve mantıklı olmasını da seviyorum. Her şeyi bir sözlüğe sokmak, anlamı gizleyen bir kolaylık olabilir.
jaydel

5

Bir taleple ilgili her türlü bilgi olduğu için bir sınıfı tavsiye ederim. Sözlük kullanacak olsaydı, depolanan verilerin doğası gereği çok daha benzer olmasını beklerdim. Kendime uymaya eğilimli olduğum bir kılavuz, tüm anahtar-> değer çiftleri kümesi üzerinde döngü yapmak ve bir şeyler yapmak istersem, bir sözlük kullanmamdır. Aksi takdirde, veriler temel bir anahtar-> değer eşlemesinden çok daha fazla yapıya sahiptir, bu da bir sınıfın muhtemelen daha iyi bir alternatif olacağı anlamına gelir.

Bu nedenle, sınıfa bağlı kalın.


2
Ben kesinlikle katılmıyorum. Sözlüklerin kullanımını, tekrarlanacak şeylerle sınırlamak için hiçbir neden yok. Bir haritayı korumak içindir.
Katriel

1
Biraz daha yakından okuyun lütfen. Dediğim üzerinde döngü isteyebilirsiniz değil, will . Bana göre sözlük kullanmak, anahtarlar ve değerler arasında büyük bir işlevsel benzerlik olduğunu ima eder. Örneğin, yaş içeren isimler. Bir sınıf kullanmak, çeşitli 'anahtarların' çok farklı anlamlara sahip değerlere sahip olduğu anlamına gelir. Örneğin, bir PErson sınıfı alın. Burada, 'isim' bir dizge olabilir ve 'arkadaşlar' bir liste, bir sözlük veya başka bir uygun nesne olabilir. Bu sınıfın normal kullanımının bir parçası olarak tüm bu özniteliklerin üzerinden geçemezsiniz.
Stigma

1
Python'da sınıflar ve sözlükler arasındaki ayrımın, birincisinin ikincisi ('yuvalar' dayanmayan) kullanılarak gerçekleştirilmesi gerçeğiyle belirsiz olduğunu düşünüyorum. Dili ilk öğrenirken bunun kafamı biraz karıştırdığını biliyorum (sınıfların nesneler olduğu ve dolayısıyla bazı gizemli meta sınıfların örnekleri olduğu gerçeğiyle birlikte).
martineau

4

Elde etmek istediğiniz tek şey obj.bla = 5yerine sözdizimi şekeri ise obj['bla'] = 5, özellikle de bunu çok tekrarlamak zorunda kalırsanız, martineaus önerisinde olduğu gibi bazı sade konteyner sınıfları kullanmak isteyebilirsiniz. Yine de, oradaki kod oldukça şişirilmiş ve gereksiz yere yavaş. Bunu şu şekilde basit tutabilirsiniz:

class AttrDict(dict):
    """ Syntax candy """
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

namedtupleS veya bir sınıfa geçmenin bir başka nedeni __slots__de bellek kullanımı olabilir. Dicts, liste türlerinden önemli ölçüde daha fazla bellek gerektirir, bu nedenle üzerinde düşünülmesi gereken bir nokta olabilir.

Her neyse, özel durumunuzda, mevcut uygulamanızdan vazgeçmek için herhangi bir motivasyon yok gibi görünüyor. Görünüşe göre bu nesnelerin milyonlarcasını tutmuyorsunuz, bu nedenle listeden türetilmiş türlere gerek yok. Ve aslında içinde bazı işlevsel mantık içeriyor __init__, bu yüzden de sahip olmaman gerekiyor AttrDict.


types.SimpleNamespace(Python 3.3'ten beri mevcuttur) özel AttrDict'e bir alternatiftir.
Cristian Ciupitu

4

Pastanızı alıp yemek de mümkün olabilir. Başka bir deyişle, hem bir sınıfın hem de sözlük örneğinin işlevselliğini sağlayan bir şey yaratabilirsiniz. ActiveState'in Dɪᴄᴛɪᴏɴᴀʀʏ ᴡɪᴛʜ ᴀᴛᴛʀɪʙᴜᴛᴇ-sᴛʏʟᴇ ᴀᴄᴄᴇss tarifine ve bunu yapmanın yolları hakkındaki yorumlara bakın.

Bir alt sınıf yerine normal bir sınıf kullanmaya karar verirseniz, Tʜᴇ sɪᴍᴘʟᴇ ʙᴜᴛ ʜᴀɴᴅʏ "ᴄᴏʟʟᴇᴄᴛᴏʀ ᴏғ ᴀ ʙᴜɴᴄʜ ᴏғ ɴᴀᴍᴇᴅ sᴛᴜғғ" ᴄʟᴀss tarifinin (Alex Martelli tarafından) çok esnek ve bu tür şeyler için yararlı olduğunu buldum yaptığınız gibi görünüyor (yani göreceli basit bir bilgi toplayıcısı oluşturun). Bir sınıf olduğu için, yöntemler ekleyerek işlevselliğini daha da genişletebilirsiniz.

Son olarak, sınıf üyelerinin adlarının yasal Python tanımlayıcıları olması gerektiği, ancak sözlük anahtarlarının olmadığı unutulmamalıdır - bu nedenle, bir sözlük bu konuda daha fazla özgürlük sağlar, çünkü anahtarlar karma bir şey olabilir (dizge olmayan bir şey bile).

Güncelleme

Python 3.3 modülüne bir alt sınıf object(a sahip olmayan __dict__) adlı bir sınıf SimpleNamespaceeklenmiştir typesve bu da başka bir alternatiftir.


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.