Python'un mirası “is-a” mirası mı yoksa kompozisyon tarzı mı?


10

Python'un çoklu kalıtıma izin verdiği göz önüne alındığında, Python'daki deyimsel kalıtım neye benziyor?

Java gibi tek kalıtıma sahip dillerde, bir nesnenin başka bir nesnenin "a-a" olduğunu ve nesneler arasında (üst nesneden alt nesneye) kod paylaşmak istediğinizde kalıtım kullanılır. Örneğin, diyebiliriz Dogbir olduğunu Animal:

public class Animal {...}
public class Dog extends Animal {...}

Ancak Python çoklu kalıtım desteklediğinden, diğer birçok nesneyi bir araya getirerek bir nesne oluşturabiliriz. Aşağıdaki örneği düşünün:

class UserService(object):
    def validate_credentials(self, username, password):
        # validate the user credentials are correct
        pass


class LoggingService(object):
    def log_error(self, error):
        # log an error
        pass


class User(UserService, LoggingService):
    def __init__(self, username, password):
        self.username = username
        self.password = password

    def authenticate(self):
        if not super().validate_credentials(self.username, self.password):
            super().log_error('Invalid credentials supplied')
            return False
         return True

Bu, Python'da çoklu kalıtımın kabul edilebilir veya iyi bir kullanımı mı? Kalıtım demek, bir nesnenin başka bir nesnenin "a-" olduğu zaman User, UserServiceve öğelerinden oluşan bir model yaratırız LoggingService.

Veritabanı veya ağ işlemleri için tüm mantık User, UserServicenesneye yerleştirilerek modelden ayrı tutulabilir ve oturum açmak için tüm mantık saklanabilir LoggingService.

Bu yaklaşımla ilgili bazı sorunlar görüyorum:

  • Bu bir Tanrı nesnesi yaratıyor mu? Beri Usermiras kaldığı veya onlardan oluştuğu UserServiceve LoggingServicegerçekten tek sorumluluk ilkesini takip ettiğinden mi?
  • Bir üst / sonraki satır içi nesnede yöntemlere erişmek için (örneğin, UserService.validate_credentialskullanmamız gerekir super. , örnekleme UserServiceve benzeri bir şey yapmaself.user_service.validate_credentials

Yukarıdaki kodu uygulamanın Pythonic yolu ne olurdu?

Yanıtlar:


9

Python'un mirası “is-a” mirası mı yoksa kompozisyon tarzı mı?

Python her iki stili de destekler. Bir kullanıcının bir kaynaktan günlük işlevselliğine ve başka bir kaynaktan kimlik doğrulamasına sahip olduğu kompozisyon ilişkisi ilişkisini gösteriyorsunuz. LoggingServiceVe UserServicebazlar Katmalar: onlar işlevsellik sağlamak ve kendileri tarafından örneği olması amaçlanmamıştır.

Türdeki karışımları oluşturarak, Günlüğe kaydedebilen, ancak kendi örnekleme işlevini eklemesi gereken bir Kullanıcıya sahip olursunuz.

Bu, kişinin tek mirasa bağlı kalamayacağı anlamına gelmez. Python da bunu destekliyor. Geliştirme yeteneğiniz çoklu mirasın karmaşıklığıyla engellenirse, kendinizi daha rahat hissedene veya tasarımda değiş tokuş etmeye değer olduğuna inandığınız bir noktaya gelene kadar bundan kaçınabilirsiniz.

Bu bir Tanrı nesnesi yaratıyor mu?

Günlüğe kaydetme biraz teğet gibi görünüyor - Python'un bir günlükçü nesnesine sahip kendi günlükleme modülü var ve kural modül başına bir günlükçünün olması.

Ancak kayıt modülünü bir kenara koyun. Belki de bu tek sorumluluğu ihlal eder, ancak belki de kendi bağlamınızda bir Kullanıcı tanımlamak çok önemlidir. Sorumluluğu takdirsizleştirmek tartışmalı olabilir. Ancak daha geniş prensip, Python'un kullanıcıya karar vermesine izin vermesidir.

Süper daha az net mi?

superyalnızca aynı ada sahip bir işlevin içinden Yöntem Çözüm Sırası'nda (MRO) bir üst öğeye yetki vermeniz gerektiğinde gereklidir. Ebeveynin yöntemine yapılan bir çağrıyı kodlamak yerine kullanmak en iyi yöntemdir. Ama ebeveyni sabit kodlamayacaksanız, ihtiyacınız yok super.

Buradaki örneğinizde, sadece yapmanız gerekir self.validate_credentials. selfsizin açınızdan daha net değil. İkisi de MRO'yu takip ediyor. Her birini uygun yerlerde kullanırım.

Eğer denilen olsaydı authenticate, validate_credentialsbunun yerine, kullanmak için gerekli olurdu super(veya sabit kod ebeveyn) bir tekrarlama hatasını önlemek için.

Alternatif Kod önerisi

Anlambilimin iyi olduğunu varsayarak (logging gibi), sınıfta yapacağım şey User:

    def validate_credentials(self): # changed from "authenticate" to 
                                    # demonstrate need for super
        if not super().validate_credentials(self.username, self.password):
            # just use self on next call, no need for super:
            self.log_error('Invalid credentials supplied') 
            return False
        return True

1
Katılmıyorum. Kalıtım her zaman alt sınıflarında bir sınıfın genel arabiriminin bir kopyasını oluşturur. Bu bir "has-a" ilişkisi değildir. Bu alt tip, sade ve basittir ve bu nedenle açıklanan uygulama için uygun değildir.
Jules

@Jules Neye katılmıyorsunuz? Gösterilebilecek birçok şey söyledim ve mantıklı bir şekilde sonuçlandı. Sen olan sen, derken yanlış "Kalıtım hep onun alt sınıfları bir ortak arabirim sınıfının bir kopyasını oluşturur." Python'da kopya yoktur - yöntemler, C3 algoritması yöntemi çözünürlük sırasına (MRO) göre dinamik olarak aranır.
Aaron Hall

1
Mesele, uygulamanın nasıl çalıştığına ilişkin spesifik ayrıntılar değil, sınıfın genel arayüzünün nasıl göründüğü ile ilgilidir. Örnek olması durumunda, Usernesnelerin arayüzlerinde sadece Usersınıfta tanımlanan üyeler değil , UserServiceve ve öğelerinde tanımlananlar da vardır LoggingService. Bu değil kamu arabirim (değil doğrudan kopyalama yoluyla değil, süper sınıf arayüzler için dolaylı arama ile de olsa) kopyalanması nedeniyle, bir "has-a" ilişkisi.
Jules

Has-a Kompozisyon anlamına gelir. Mixinler bir Kompozisyon şeklidir. Kullanıcı sınıfı olan bir UserService veya LoggingService değil, ama sahip olduğu işlevsellik. Bence Python'un mirası, Java'dan fark ettiğinden daha farklı.
Aaron Hall

@AaronHall Aşırı basitleştiriyorsunuz (bu , şans eseri bulduğum diğer cevaplarla çelişme eğilimindedir ). Alt tür ilişkisi açısından, Kullanıcı hem UserService hem de LoggingService'dir. Şimdi, buradaki ruh, bir Kullanıcının böyle ve bu gibi işlevlere sahip olması için işlevsellikler oluşturmaktır. Genel olarak karışımların çoklu kalıtımla uygulanması gerekmez. Ancak Python'da bunu yapmanın olağan yolu budur.
coredump

-1

Birden çok üst sınıfa izin verdiği gerçeğinden başka, Python'un mirası Java'lardan önemli ölçüde farklı değildir, yani bir alt sınıfın üyeleri de kendi süpertiplerinin her birinin üyesidir [1]. Python'un ördek yazmayı kullanması da bir fark yaratmaz: alt sınıfınız üst sınıflarının tüm üyelerine sahiptir, bu nedenle bu alt sınıfları kullanabilen herhangi bir kod tarafından kullanılabilir. Kompozisyon kullanılarak çoklu kalıtımın etkili bir şekilde uygulanması kırmızı bir ringa balığıdır: bir sınıfın özelliklerinin diğerine otomatik olarak kopyalanması konudur ve kompozisyonun kullanılması ya da sadece üyelerin nasıl varsayıldığını sihirli bir şekilde tahmin etmesi önemli değildir. çalışmak: onlara sahip olmak yanlıştır.

Evet, bu tek sorumluluğu ihlal ediyor, çünkü nesnelerinize, mantıksal olarak yaptıklarının bir parçası olmayan eylemleri gerçekleştirme yeteneği veriyorsunuz. Evet, aynı şeyi söylemenin başka bir yolu olan "tanrı" nesneleri yaratır.

Python'da nesne yönelimli sistemler tasarlarken, Java tasarım kitapları tarafından varolan aynı maksimum değer de geçerlidir: kompozisyonu kalıtım olarak tercih edin. Aynısı, çoklu kalıtıma sahip (çoğu [2]) diğer sistemler için de geçerlidir.

[1]: "gerçek-bir" ilişki diyebilirsin, ama terimden kişisel olarak hoşlanmıyorum, çünkü gerçek dünyayı modelleme fikrini öneriyor ve nesne yönelimli modelleme gerçek dünyayla aynı değil.

[2]: C ++ hakkında pek emin değilim. C ++, devralınan sınıfın genel üyelerini kullanmak istediğinizde bir alan adı belirtmeye gerek kalmadan, esasen kompozisyon olan "özel kalıtım" ı destekler. Sınıfın genel arayüzünü hiç etkilemez. I do not gibi kullanmaktan ama ben herhangi iyi bir sebep göremiyorum.

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.