dziedziczenie warunkowe

Odpowiedz Nowy wątek
2019-11-07 22:06
0

Witam wszystkich

Problem polega na tym, że mamy w pewnym module C.py klasę C która dziedziczy z klasy X.
Sęk w tym, że klasa X importowana z innego modułu tutaj ABX.py powinna w zależności od parametrów inicjalizacyjnych klasy C
niejako przekazać do klasy C nie samą siebie ale inną klasę. I tak gdy parametr typ przekazywany do klasy C jest "A"
to klasa pośrednicząca X powinna transformować się w klasę A, a gdy parametr jest "B" to klasa X powinna transformować się w B.
Ważne jest tutaj aby instancja klasy C w zależności od przekazanego parametru, który decyduje jaka klasa będzie dziedziczona
zachowała wszystkie swoje właściwości czyli właściwości klasy C jaki właściwości klasy z której będzie dziedziczyć warunkowo czyli klasy A lub B.
A klasa X ma się po prostu rozpłynąć kończąc swoje zadanie :)

Reasumując nie wiem jak napisać kod klasy X aby spełnione były warunki na końcu modułu C.py

To co jest zamieszczone poniżej to tylko nieudana próba osiągnięcia tego.
Chyba że macie jakiś inny pomysł.

## moduł ABX.py

# możemy modyfikować dowolnie kod klasy A,B,X, jak i ten moduł.
class A(object): pass
class B(object): pass

class X(object):
    def __init__(self, typ):
        if   typ == "A":
            # Tutaj przykładowy kod, który w miejsce klasy X wstawia klase A
            self.__class__ = A
        elif typ == "B":
            # Tutaj przykładowy kod, który w miejsce klasy X wstawia klase B
            self.__class__ = B
## moduł C.py

from ABC import X

# Tej klasy nie wolno ruszać, ani zasadnizco tego modułu
class C(X):pass

c = C("A")                  # mają być spełnione poniższe warunki
print isinstance(c, C) #True instancja musi dziedziczyć po klasie C
print isinstance(c, X) #False
print isinstance(c, A) #True instancja musi dziedziczyć w tym wypadku również po klasie A
print isinstance(c, B) #False

c = C("B")
print isinstance(c, C) #True instancja musi dziedziczyć po klasie C
print isinstance(c, X) #False
print isinstance(c, A) #False
print isinstance(c, B) #True instancja musi dziedziczyć w tym wypadku również po klasie B
edytowany 5x, ostatnio: deRoble, 2019-11-07 22:18
Sorki. Zamiast "from ABC import X" powinno być "from ABX import X, A, B" - deRoble 2019-11-07 23:42

Pozostało 580 znaków

2019-11-07 22:16
2

Jak klasa ma się "transformować" w inną, nie wiem po co to Robisz, ale nie lepiej użyć funkcji, która, w zależności od parametru, prozaicznie zwraca obiekt danej klasy?


Pozostało 580 znaków

2019-11-07 22:35
0
lion137 napisał(a):

Jak klasa ma się "transformować" w inną, nie wiem po co to Robisz, ale nie lepiej użyć funkcji, która, w zależności od parametru, prozaicznie zwraca obiekt danej klasy?

Mój kod powoduje transformację, tylko problem polega na tym, że

c = C("A")                 
print isinstance(c, C) # tutaj otrzymujemy False 
print isinstance(c, A) # a tutaj otrzymujemy True 

czyli tracimy właściwości klasy C
a zmienna c staje się instancją ale tylko klasy A

a mnie nie o to chodzi, ale o to aby kod

c = C("A")                 

powodował, że klasa C dziedziczy z A podczas inicjalizacji

Poco to robię?
Po prostu mam taki praktyczny problem. To co tutaj podaję jest tylko tego uproszczeniem.
Nie mogę ruszyć pewnego modułu tutaj reprezentowanego przykładowo przez C.py.
Dlatego nie mogę do tego wykorzystać funkcji

edytowany 3x, ostatnio: deRoble, 2019-11-07 22:40
Pokaż pozostałe 2 komentarze
print isinstance(c, C) -> SyntaxError: invalid syntax - enedil 2019-11-07 22:47
@enedil bo to python 2 - iksde 2019-11-07 22:48
@iksde: co to Python 2? - enedil 2019-11-07 22:50
Tak to python 2.7 - deRoble 2019-11-07 22:56
Tutaj nie ma żadnego mojego designu jest tylko problem. Przypominam w module C.py nie można grzebać - deRoble 2019-11-07 22:58

Pozostało 580 znaków

2019-11-07 22:54
0

Możesz takiego potworka wyczarować:

class A:
    pass

class B:
    pass

class X:
    pass

class XA(X, A):
    pass

class XB(X, B):
    pass

class Magic:
    def __new__(cls, arg):
        if arg == 'A':
            return XA()
        else:
            return XB()

a = Magic('A')
b = Magic('B')

print(isinstance(a, A))  # True
print(isinstance(a, X))  # True
print(isinstance(b, B))  # True
print(isinstance(b, X))  # True

ale nie polecam ;)

Aaaa, hacki, hacki, straszne hacki:) - lion137 2019-11-07 22:55
Z ciekawości chciałem sprawdzić, czy (i jak) to działa :D tak jak napisałem, nie polecam takich kombinacji - w życiu bym takiego kodu nie użył poza REPLem. - iksde 2019-11-07 22:56

Pozostało 580 znaków

2019-11-07 23:31
0
iksde napisał(a):

Możesz takiego potworka wyczarować:

class A:
  pass

class B:
  pass

class X:
  pass

class XA(X, A):
  pass

class XB(X, B):
  pass

class Magic:
  def __new__(cls, arg):
      if arg == 'A':
          return XA()
      else:
          return XB()

a = Magic('A')
b = Magic('B')

print(isinstance(a, A))  # True
print(isinstance(a, X))  # True
print(isinstance(b, B))  # True
print(isinstance(b, X))  # True

ale nie polecam ;)

Dzięki ale to nie pasuje do mojego problemu. Ponieważ musiał bym mieć możliwość dowolnie modyfikować kod.

W moim, niemogącym ulec zmianie module C.py jest import ABX.py.
Należy napisać kod do tego ABX.py tak, aby uruchamiając C.py były spełnione założenia.

c = C("A")                  # mają być spełnione poniższe warunki
print isinstance(c, C) #True instancja musi dziedziczyć po klasie C
print isinstance(c, X) #False
print isinstance(c, A) #True instancja musi dziedziczyć w tym wypadku również po klasie A
print isinstance(c, B) #False

c = C("B")
print isinstance(c, C) #True instancja musi dziedziczyć po klasie C
print isinstance(c, X) #False
print isinstance(c, A) #False
print isinstance(c, B) #True instancja musi dziedziczyć w tym wypadku również po klasie B

Taka mała errata

#w module C.py jest    
from ABC import X 
#a powinno być
from ABX import X, A, B

Pozostało 580 znaków

2019-11-08 01:23
1

Doszedłem do czegoś takiego

# abx.py

import inspect

_classes = {}

def register(class_):
    _classes[class_.__name__] = class_
    return class_

@register
class A(object): pass

@register
class B(object): pass

class X(object):
    def __init__(self, arg):
        self.__class__ = type(arg, (self.__class__, _classes[arg]), {})
# C.py

from ABX import X, A, B

# C.py
class C(X): pass

c = C("A")
assert isinstance(c, C)
assert isinstance(c, A)
assert not isinstance(c, B)
c = C("B")
assert isinstance(c, C)
assert not isinstance(c, A)
assert isinstance(c, B)

I działa, ale tego warunku assert not isinstance(c, X) nie jestem w stanie spełnić i sprawić żeby X "zniknęło", nie kojarzę też żadnego sposobu na to by to zrobić.

Są to w ogóle okropne hacki, cokolwiek chcesz robić, robisz to źle.

edytowany 1x, ostatnio: Spearhead, 2019-11-08 02:17

Pozostało 580 znaków

2019-11-08 01:58
0

Co za magia! Chyba tej nocy nie zdążę zakumać, jak to w ogóle może działać, bo mi się już oczy kleją :)
Ale to już wygląda na to o co chodzi. Pewnie dałoby się zrobić prościej i czytelniej.
Ja już prawie mam swój prostszy pomysł, ale jeszcze się nim nie pochwalę, bo mogę spalić.
W sumie te trzy warunki, które są spełnione, to mi w zupełności wystarczają, a ten assert not isinstance(c, X)
to nie musi być spełniony, o ile nie zaburzy prawidłowego korzystania z klasy C, przysłaniając jej metody czy właściwości.
A wydaje się, że nie zaburzy.

edytowany 1x, ostatnio: deRoble, 2019-11-08 02:04

Pozostało 580 znaków

2019-11-08 02:09
0

Działa to tak, że type pozwala na dynamiczne tworzenie klas:

class Base(object):
    pass

Nazwa = type('Nazwa', (Base,), {})

Jest równoważne

class Base(object):
    pass

class Nazwa(Base):
    pass

Zatem w momencie tworzenia klasy podmieniam jej atrybut __class__ na dynamicznie stworzoną klasę. Uwaga - ponieważ za każdym razem tworzę nowy typ klasy, prowadzi to do dziwnych efektów ubocznych pokroju type(C("A")) == type(C("A")) zwraca False (to akurat można by naprawić trzymając cache typów) - dlatego właśnie takich hacków się nie robi, może cię to uderzyć mocno tam, gdzie się nie będziesz spodziewał.

Dekorator register służy tylko po to, by zebrać dekorowane klasy A, B do słownika _modules, żebym mógł je podać na liście rodziców nowego typu zamiast stosować jakąś ifologię if arg == 'A'.

EDIT

Wspomniana wersja z cache typów, dla której type(C("A")) == type(C("A")) daje True.

import inspect

_classes = {}
_types = {}

def register(class_):
    _classes[class_.__name__] = class_
    return class_

@register
class A(object): pass

@register
class B(object): pass

class X(object):
    def __init__(self, arg):
        type_ = _types.setdefault(arg, type(arg, (self.__class__, _classes[arg]), {}))
        self.__class__ = type_

Jakieś inne nieoczywiste efekty uboczne pewnie też tu są xD

edytowany 4x, ostatnio: Spearhead, 2019-11-08 02:17

Pozostało 580 znaków

2019-11-08 08:59
0

No to zaryzykuje rozwiązanie, które wydaje mi się najprostsze.
Przyśniło mi się dzisiejszej nocy :)
Poniżej dla jeszcze większej czytelności zamieszczam jedno modułowy kod ale oddający problem.

# _*_ coding: utf-8  _*_
class A(object): pass
class B(object): pass

class X(object):
    def __new__(Cls, typ):
        if   typ == "A":
            Cls.__bases__= (X,A,)
        elif typ == "B":
            Cls.__bases__= (X,B,)
        return super(X, Cls).__new__(Cls)

# poniżej nie ruszamy kodu !!! a powyżej możemy
class C(X):pass

c = C("A")
assert isinstance(c, C) #instancja dziedziczy po klasie C
assert isinstance(c, A) #instancja dziedziczy także po klasie A
assert not isinstance(c, B) #instancja nie dziedziczy po B

c = C("B")
assert isinstance(c, C) #instancja dziedziczy po klasie C
assert not isinstance(c, A) #instancja nie dziedziczy po A
assert isinstance(c, B) #instancja dziedziczy również po klasie B

print "To działa !!!"

Proszę napiszcie co o tym sądzicie i czym to grozi :O
Jakby ktoś miał jeszcze jakieś fajne pomysły to jestem ich ciekaw.

edytowany 10x, ostatnio: deRoble, 2019-11-08 18:08

Pozostało 580 znaków

2019-11-09 09:37
0

Niestety mój kod tylko pozornie działa dobrze. W rzeczywistości nie nadaje się do wykorzystania.

# _*_ coding: utf-8  _*_
class A(object): pass
class B(object): pass

class X(object):
    def __new__(Cls, typ):
        if   typ == "A":
            Cls.__bases__= (X,A,)
        elif typ == "B":
            Cls.__bases__= (X,B,)
        return super(X, Cls).__new__(Cls)

# poniżej nie ruszamy kodu !!! a powyżej możemy
class C(X):pass

a = C("A")
assert isinstance(a, A) #instancja teraz dziedziczy A
assert not isinstance(a, B) #instancja nie dziedziczy po B

b = C("B")
assert not isinstance(b, A) #instancja nie dziedziczy po A
assert isinstance(b, B) #instancja dziedziczy po B

#teraz sprawdzamy czy "a" dalej jest instancją A
assert not isinstance(a, A) #i niestety nie jest 
assert isinstance(a, B) #ponieważ teraz jest instancją B
edytowany 3x, ostatnio: deRoble, 2019-11-09 09:52

Pozostało 580 znaków

2019-11-10 15:03
0

W związku z trudnościami w realizacji mojej idei warunkowego dziedziczenia klas zostałem zmuszony do szukania innego sposobu.
Ten sposób okazał się trywialny poprzez zastosowanie warunkowej agregacji klas a nie dziedziczenia.
Plus tego rozwiązania to to, że żadnych niespodzianek tutaj nie będzie.

# _*_ coding: utf-8  _*_
class A(object): pass
class B(object): pass

class X(object):
    def __init__(self, typ):
        if typ == "A":
            self.x = A()
        elif typ == "B":
            self.x = B()

# poniżej nie ruszamy kodu !!! a powyżej możemy
class C(X): pass

a = C("A")
print a.x
b = C("B")
print b.x

Jednakże nie odpuszczam tematu idei warunkowego dziedziczenia klas za pośrednictwem pewnej klasy pośredniczącej, u mnie X.
Mam kolejne rozwiązanie, które przedstawię w następnym poście a które nie zawiera wad z poprzedniego.

edytowany 3x, ostatnio: deRoble, 2019-11-10 15:07

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0