Python'da başka bir sınıfın içinde bir sınıf tanımlamanın bir faydası var mı?


108

Burada bahsettiğim şey iç içe sınıflar. Esasen, modellediğim iki sınıfım var. Bir DownloadManager sınıfı ve bir DownloadThread sınıfı. Buradaki bariz OOP kavramı kompozisyondur. Bununla birlikte, kompozisyon mutlaka yuvalama anlamına gelmez, değil mi?

Şuna benzer bir kodum var:

class DownloadThread:
    def foo(self):
        pass

class DownloadManager():
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadThread())

Ama şimdi iç içe geçmenin daha iyi olacağı bir durum olup olmadığını merak ediyorum. Gibi bir şey:

class DownloadManager():
    class DownloadThread:
        def foo(self):
            pass
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadManager.DownloadThread())

Yanıtlar:


125

Bunu, "iç" sınıf bir defaya mahsus olduğunda ve dış sınıfın tanımının dışında asla kullanılmayacak olduğunda yapmak isteyebilirsiniz . Örneğin, bir metasınıf kullanmak bazen

class Foo(object):
    class __metaclass__(type):
        .... 

bir meta sınıfı ayrı ayrı tanımlamak yerine, yalnızca bir kez kullanıyorsanız.

Bunun gibi iç içe geçmiş sınıfları kullandığım diğer tek seferde, yakından ilişkili bir grup sınıfı birlikte gruplamak için dış sınıfı yalnızca bir ad alanı olarak kullandım:

class Group(object):
    class cls1(object):
       ...

    class cls2(object):
       ...

Daha sonra başka bir modülden, Group'u içe aktarabilir ve bunlara Group.cls1, Group.cls2 vb. Olarak başvurabilirsiniz. Ancak, bir modül kullanarak tam olarak aynı şeyi (belki daha az kafa karıştırıcı bir şekilde) yapabileceğiniz iddia edilebilir.


13
İkinci durumda, ad alanları olarak tasarlanmış bir modül veya paket kullanarak -belirsiz olarak- daha iyi hizmet alacağınızı iddia ediyorum.
Matthew Trevor

5
Bu harika bir cevap. Basit, isabetli ve modülleri kullanmanın alternatif yönteminden bahsediyor. +1
CornSmith

5
Sadece kayıt için, kodlarını hiyerarşik bir paket ve uygun modüller halinde inatla reddeden veya düzenleyemeyenler için, sınıfları bir ad alanı olarak kullanmak bazen hiçbir şey kullanmamaktan daha iyi olabilir.
Acumenus

20

Python bilmiyorum ama sorunuz çok genel görünüyor. Python'a özelse beni görmezden gelin.

Sınıf yuvalama tamamen kapsamla ilgilidir. Bir sınıfın yalnızca başka bir sınıf bağlamında anlam ifade edeceğini düşünüyorsanız, ilk sınıf muhtemelen iç içe bir sınıf olmak için iyi bir adaydır.

Yardımcı sınıfları özel, iç içe sınıflar olarak yapan yaygın bir kalıptır.


9
Kayıt için not etmek gerekirse, privatesınıf kavramı Python'da pek uygulanmaz, ancak zımni kullanım kapsamı hala kesinlikle geçerlidir.
Acumenus

7

Gelişmiş işlevsellikleri belirli bir iç içe geçmiş sınıfta kapsüllenmiş olan miras alınan sınıflar oluşturmak istendiğinde, iç içe geçmiş sınıf için başka bir kullanım vardır.

Bu örneğe bakın:

class foo:

  class bar:
    ...  # functionalities of a specific sub-feature of foo

  def __init__(self):
    self.a = self.bar()
    ...

  ...  # other features of foo


class foo2(foo):

  class bar(foo.bar):
    ... # enhanced functionalities for this specific feature

  def __init__(self):
    foo.__init__(self)

Yapıcısında foo, satırın self.a = self.bar(), inşa foo.baredilmekte olan nesne aslında foobir foo2.barnesne olduğunda bir a ve inşa edilen nesne aslında bir foo2nesne olduğunda bir nesne oluşturacağına dikkat edin.

Sınıf Eğer barsınıfın dışında tanımlanan fooyerine yanı sıra (aranmak onun miras versiyonu bar2örneğin), o zaman yeni bir sınıf tanımlama foo2içinde constuctor çünkü çok daha acı verici olurdu foo2onun ilk satırı yerine olması gerekir self.a = bar2(), bu tüm kurucunun yeniden yazılması anlamına gelir.


6

Metasınıflarla uğraşmanız dışında, bunu yapmanın gerçekten bir faydası yok.

sınıf: süit gerçekten düşündüğünüz gibi değil. Bu tuhaf bir kapsam ve tuhaf şeyler yapıyor. Gerçekten bir sınıf bile oluşturmuyor! Bu sadece bazı değişkenleri toplamanın bir yoludur - sınıfın adı, tabanları, küçük bir öznitelik sözlüğü ve bir metasınıf.

Ad, sözlük ve tabanların tümü metasınıf olan işleve aktarılır ve ardından sınıf: süitin bulunduğu kapsamdaki 'ad' değişkenine atanır.

Metasınıflarla uğraşarak ve gerçekten de stok standart sınıflarınızdaki sınıfları iç içe geçirerek kazanabileceğiniz şey, kodu okumak daha zordur, kodu anlamak daha zordur ve neden "sınıf" a aşina olmadan anlaşılması son derece zor olan garip hatalardır. kapsam, diğer herhangi bir python kapsamından tamamen farklıdır.


3
Katılmıyorum: İstisna sınıflarını yerleştirmek çok kullanışlıdır, çünkü sınıfınızın kullanıcıları herhangi bir karmaşık içe aktarma işlemi yapmak zorunda kalmadan özel istisnalarınızı kontrol edebilir. Ör.except myInstanceObj.CustomError, e:
RobM

2
@Jerub, neden bu kadar kötü?
Robert Siemer

5

Sınıf üreteci olarak bir sınıf kullanıyor olabilirsiniz. Gibi (bazı manşet kodunda :)

class gen(object):
    class base_1(object): pass
    ...
    class base_n(object): pass

    def __init__(self, ...):
        ...
    def mk_cls(self, ..., type):
        '''makes a class based on the type passed in, the current state of
           the class, and the other inputs to the method'''

Bu işleve ihtiyaç duyduğunuzda size çok açık geleceğini düşünüyorum. Buna benzer bir şey yapmanız gerekmiyorsa, muhtemelen iyi bir kullanım durumu değildir.


1

Hayır, kompozisyon yuvalama anlamına gelmez. Dış sınıfın ad alanında daha fazla gizlemek istiyorsanız iç içe geçmiş bir sınıfa sahip olmak mantıklı olacaktır.

Her neyse, davanıza yerleştirmek için pratik bir kullanım göremiyorum. Kodun okunmasını (anlaşılmasını) zorlaştıracak ve aynı zamanda satırları kısaltacak ve bölünmeye daha yatkın hale getirecek girintiyi artıracaktır.

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.