Search code examples
pythonpython-3.xcastingpolymorphismdowncast

Convert derived class to base class in Python


class Option:
    order_type: str
    pair: str

    def __init__(self, pair, order_type) -> None:
        self.order_type = order_type
        self.pair = pair

class Order(Option):

    price: float
    quantity: float
    
    def __init__(self, pair, order_type, price, quantity) -> None:
        Option.__init__(self, pair, order_type)
        self.price = price
        self.quantity = quantity


order = Order("btcusdt", "sell", "1", "1")

I want to get option from order object like.

option = order as Option
order.option

Solution

  • For those landing here for the same question, but not specific to this example, use super(...). It's usually used within the class member functions (check correct way to use super (argument passing) ), but here are some examples to use it from outside the class:

    # create some classes with inheritance to test
    class A:
        x="a"
    class B(A):
        x="b"
    class C(B):
        x="c"
    class D(C):
        x="d"
        
    # Casting types to parents via explicit class name
    assert A().x=='a'
    assert B().x=='b'
    assert super(B, B()).x=='a' # << cast B to A
    assert super(C, C()).x=='b' # << cast C to B
    assert super(B, C()).x=='a' # << cast C to A
    assert super(D, D()).x=='c' # << cast D to C
    assert super(C, D()).x=='b' # << cast D to B
    assert super(B, D()).x=='a' # << cast D to A
    
    # create an unrelated class
    class X:
        x="x"
    
    # cast C to X fails because C is unrelated to X
    try:
        super(X, B())
    except TypeError as e:
        assert str(e)=="super(type, obj): obj must be an instance or subtype of type"
    
    # Casting types to parent via "__bases__"
    # Ref:
    #   List all base classes in a hierarchy of given class?
    #   https://stackoverflow.com/questions/1401661/list-all-base-classes-in-a-hierarchy-of-given-class
    assert A.__bases__==(object,)
    assert B.__bases__==(A,)
    assert C.__bases__==(B,)
    assert D.__bases__==(C,)
    assert super(C.__bases__[0], C()).x=='a' # << cast C to A
    assert super(D.__bases__[0], D()).x=='b' # << cast D to B
    assert super(D.__bases__[0].__bases__[0], D()).x=='a' # << cast D to A
    
    # Note that "type(super(...))" returns "super" instead of the class name
    # Not sure how to fix that (maybe in combination with __bases__?)
    assert type(super(B, B()))==super
    
    # Casting ***instances*** to parents (super(...) returns types, not instances)
    b1=B()
    assert b1.x=="b"
    b1.__class__=A # cast instance of B to A with __class__, ref: https://stackoverflow.com/a/50465583/4126114
    assert b1.x=="a"
    
    # create multi-class inheritance (inherit from two classes)
    class E(C, X):
        x="e"
        
    # Casting types
    assert E().x=='e'
    assert super(E, E()).x=='c' # cast E to C
    
    # cast type E to X via super doesnt work
    try:
        super(X, E()).x
    except AttributeError as e:
        assert str(e)=="'super' object has no attribute 'x'"
    
    assert super(C, E()).x=='b' # cast E to B
    assert super(B, E()).x=='a' # cast E to A
    
    # cast type E to parent of D (which is also the parent of E) doesn't work, even though they're both C
    try:
        super(D, E()).x
    except TypeError as e:
        assert str(e) == "super(type, obj): obj must be an instance or subtype of type"
    
    # Casting ***instances***
    e1=E()
    e1.__class__=C # cast E to C
    assert e1.x=="c"
    e1.__class__=X # cast E to X (it works with instances, but didn't work with types)
    assert e1.x=="x"