python equivalent to c# where T : new()
Asked Answered
H

1

6

Is there a way in python to restrict generic types to ones that can be instantiated?

I'm looking for an equivalent to C#'s

 where T : new()

I am already using type hints as follows

T = TypeVar("T", bound=Button)

And I'd like to know if there something like

 T = TypeVar("T", bound=Button,new())

For example, given that Button is an abstract class (ABC) this code should create a menu composed of any subtype of Button

class Button(ABC):
    def __init__(self) -> None:
        self._name = ""
    @abstractmethod
    def show(self) -> None:
        pass
    def set_name(self, value:str) -> None:
        self._name = value

class SquareButton(Button):
    def show(self) -> None:
        print("[",self._name,"]")

class CircleButton(Button):
    def show(self) -> None:
        print("(",self._name,")")

T = TypeVar("T", bound=Button) # restricting type here

class MenuBuilder:
    def build(self,class_type: Type[T]) -> None:
        pb1: Button = class_type()
        pb2: Button = class_type()
        pb3: Button = class_type()
        pb1.set_name("button1")
        pb2.set_name("button2")
        pb3.set_name("button3")
        pb1.show(); pb2.show(); pb3.show()

#main
mb: MenuBuilder = MenuBuilder()
mb.build(Button) # fails at run time - I want a pre-run error here
mb.build(CircleButton) # OK

As it stands now, it allows calling the method with an abstract type Button, which fails at run time when I try to create instances of it.

I am using python 3.8.1 and VS Code with pylance

Edit: One of the answers suggested replacing the Generics by Protocol to make sure Buttons functions were implemented. While this prevents sending the abstract class, it allows sending classes that don't inherit from Button. I'm looking for away to enforce both restrictions.

Haber answered 22/10, 2021 at 8:18 Comment(5)
Python doesn't really have type checking, the "equivalent" is code reviews, testing, and error handlingSoemba
The type hints in the language already help the tools restrict T to Button or its subclasses. I'm looking for something similar that will only allow the concrete subclasses.Haber
Since Menubuilder.build() is basically a staticmethod, you could instead implement it as classmethod on the Button class and just call Button.build_menu()Scevor
fwiw, mypy will raise an error hereLuge
@Luge Interesting. I hadn't considered that the problem may be that different dev tools have different support/implementations for these features.Haber
C
4

I'm not familiar with this topic, but Protocol might be the way to go here.

from typing import Protocol


class Button(Protocol):
    def show(self) -> None:
        ...

    def set_name(self, value: str) -> None:
        ...


class BaseButton(object):
    def __init__(self) -> None:
        self._name = ""

    def set_name(self, value:str) -> None:
        self._name = value


class SquareButton(BaseButton):
    def show(self) -> None:
        print("[",self._name,"]")


class CircleButton(BaseButton):
    def show(self) -> None:
        print("(",self._name,")")


class MenuBuilder:
    def build(self, class_type: Type[Button]) -> None:
        pb1: Button = class_type()
        pb2: Button = class_type()
        pb3: Button = class_type()
        pb1.set_name("button1")
        pb2.set_name("button2")
        pb3.set_name("button3")
        pb1.show()
        pb2.show()
        pb3.show()
Cassaundracassava answered 22/10, 2021 at 8:56 Comment(6)
Thanks for your answer. Using this code I get the following error from pylance: Expression of type "ButtonImpl" cannot be assigned to declared type "Button" "ButtonImpl" is incompatible with "Button"Haber
@GonenI It's what you wanted to get, isn't it ? Maybe I got your question wrong...Cassaundracassava
Sorry, I should have been more clear. You got my question right, but besides getting an error on the line in main which is what I wanted, I am also getting errors inside MenuBuilder itself. For example on the error I listed on the previous comment comes on line pb1: Button = class_type(), even with no code in mainHaber
@GonenI Oh gosh, my bad, it should be ButtonImpl instead of Button in the build function, gonna fix this in the answer :)Cassaundracassava
That's pretty cool thanks. You just forgot to add set_name to the protocol, can you update your answer? One remaining problem is that it isn't restricted to subclasses of Button anymore, any class that has these methods is allowed.Haber
@GonenI Yea that's a limitation of duck typing unfortunately. I don't have a solution in mind for this issue...Cassaundracassava

© 2022 - 2024 — McMap. All rights reserved.