-
Notifications
You must be signed in to change notification settings - Fork 769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
class properties that are other classes are not treated properly #435
Comments
Pylance is doing the right thing here. It is not legal to define a property with a classmethod. To demonstrate this, try this simple program: class MyClass:
@property
@classmethod
def my_property(cls) -> int:
return 3
a = MyClass()
print(a.my_property) This will generate an exception If you really want to create a property that works with a class (as opposed to a class instance), you need to define it on the metaclass, but I don't think that's a solution to the problem you're trying to solve here. You are also doing something that's not really correct from a type standpoint. You are declaring a property in an abstract base class and then overriding it with a variable in a subclass. A property and a variable have different semantics (e.g. a property is not writable or deletable), so pylance considers it an error if you try to override one with the other. If you replace the property declaration in your code with the following, it will work as you intend: SolutionClass: Type[SolClass] The technique you're using here is an anti-pattern because you're expecting subclasses to override a class variable. That's probably why you're finding it difficult to properly annotate the types. A more typical (and preferable) solution would look like this: from abc import ABC, abstractmethod
from typing import Type
class SolClass(ABC):
def __init__(self, x: int):
self._x = x
@abstractmethod
def getx(self) -> int:
pass
class Container(ABC):
def __init__(self, sol_class: Type[SolClass], x: int):
self.solution = sol_class(x)
def getSolution(self) -> SolClass:
return self.solution
class MySolClass(SolClass):
def getx(self) -> int:
return 2 * self._x
class UseContainer(Container):
def __init__(self, x: int):
super().__init__(MySolClass, x) |
@erictraut Thanks for your answer. The construct above was something I copied from StackOverflow here: Your solution of just declaring the variable I'm also not sure I agree with your statement " It is not legal to define a property with a classmethod." Do you have a reference for that? |
Try running the code in my post above where I combine |
Yes, I get that, but in my case, I made it an abstract class property. And that code runs fine. |
That's because you have dynamically overridden the illegal code with something else. That doesn't make it legal. It means that you've managed to mask the problem at runtime. Is there any reason you can't switch to the pattern I suggested? I think that's a much cleaner solution all around. |
This issue has been waiting for information for 30 days. Because we haven't heard back, we'll be closing this ticket. Please reopen if this issue persists! |
@savannahostrowski How can I reopen? |
The reason is because we have multiple subclasses of type So we have code like this (in addition to what I initially provided): class MySolClass2(SolClass):
def getx(self) -> int:
return 3 * self._x
class MySolClass3(SolClass):
def getx(self) -> int:
return 4 * self._x
class UseContainer2(Container):
SolutionClass = MySolClass2
def __init__(self, x: int):
super().__init__(x)
class UseContainer3(Container):
SolutionClass = MySolClass3
def __init__(self, x: int):
super().__init__(x)
d: Dict[int, Type[Container]] = {1: UseContainer, 2: UseContainer2, 3: UseContainer3}
for i in [1, 2, 3]:
uc: Container = d[i](120)
print(uc.getSolution().getx()) Your solution doesn't allow the above to work, because the line that defines |
I see, thanks for the additional context. My solution, with a minor tweak, would still work. from abc import ABC, abstractmethod
from typing import Dict, Type
class SolClass(ABC):
def __init__(self, x: int):
self._x = x
@abstractmethod
def getx(self) -> int:
pass
class Container(ABC):
def __init__(self, x: int, sol_class: Type[SolClass] = SolClass):
self.solution = sol_class(x)
def getSolution(self) -> SolClass:
return self.solution
class MySolClass(SolClass):
def getx(self) -> int:
return 2 * self._x
class UseContainer(Container):
def __init__(self, x: int):
super().__init__(x, MySolClass)
class MySolClass2(SolClass):
def getx(self) -> int:
return 3 * self._x
class MySolClass3(SolClass):
def getx(self) -> int:
return 4 * self._x
class UseContainer2(Container):
def __init__(self, x: int):
super().__init__(x, MySolClass2)
class UseContainer3(Container):
def __init__(self, x: int):
super().__init__(x, MySolClass3)
d: Dict[int, Type[Container]] = {1: UseContainer, 2: UseContainer2, 3: UseContainer3}
for i in [1, 2, 3]:
uc = d[i](120)
print(uc.getSolution().getx()) |
Still have an issue, because I would like the codes from abc import ABC, abstractmethod
from typing import Type, Dict, List, cast
class SolClass(ABC):
def __init__(self, x: int):
self._x = x
@abstractmethod
def getx(self) -> int:
pass
class Container(ABC):
@property
@classmethod
@abstractmethod
def SolutionClass(cls) -> Type[SolClass]:
pass
@property
@classmethod
@abstractmethod
def container_code(cls) -> int:
pass
def __init__(self, x: int):
self.solution = self.SolutionClass(x)
def getSolution(self) -> SolClass:
return self.solution
class MySolClass(SolClass):
def getx(self) -> int:
return 2 * self._x
class UseContainer(Container):
SolutionClass = MySolClass
container_code = 1
def __init__(self, x: int):
super().__init__(x)
class MySolClass2(SolClass):
def getx(self) -> int:
return 3 * self._x
class MySolClass3(SolClass):
def getx(self) -> int:
return 4 * self._x
class UseContainer2(Container):
SolutionClass = MySolClass2
container_code = 2
def __init__(self, x: int):
super().__init__(x)
class UseContainer3(Container):
SolutionClass = MySolClass3
container_code = 3
def __init__(self, x: int):
super().__init__(x)
container_list: List[Type[Container]] = [UseContainer, UseContainer2, UseContainer3]
d: Dict[int, Type[Container]] = {cast(int, c.container_code): c for c in container_list}
for i in d.keys():
uc: Container = d[i](120)
print(uc.getSolution().getx()) |
we are not going to support this at this time. |
+1 Would like this |
Environment data
Expected behaviour
No error
Actual behaviour
The above is valid python, and
mypy
reports no issues. But pylance reportswhich corresponds to the line:
The property in this case is callable, as it is a class.
The text was updated successfully, but these errors were encountered: