I have the following structure:
import abc
class AbstractParent:
pass
class AbstractChild(AbstractParent):
#abc.abstractmethod
def foo(self):
raise NotImplementedError()
class Child(AbstractChild):
def foo(self):
print('!')
class A:
def __init__(self, x: AbstractParent):
self.x = x
class B(A):
def __init__(self, x: Child):
super().__init__(x=x)
def test(self):
return self.x.foo() # Unresolved attribute reference 'foo' for class 'AbstractParent'
What I would expect is that since Child is a subclass of AbstractParent and Child has a method foo, and since x is being declared as a Child in class B, that foo is able to be resolved as a method of Child.
Instead Pycharm has a visual warning Unresolved attribute reference 'foo' for class 'AbstractParent'
Why isn't it able to figure out that x is a Child which has method foo?
Note that setting self.x = x in B fixes the issue. Is this a bug?
It could be solved with generics, see the following example:
from typing import Generic, TypeVar
T = TypeVar("T", bound=AbstractParent)
class A(Generic[T]):
x: T
def __init__(self, x: T):
self.x = x
class B(A[Child]):
def __init__(self, x):
super().__init__(x=x)
def test(self):
return self.x.foo()
Feel free to ask any questions.
Related
On this code:
class Obj():
def a(self):
pass
class ObjChild(Obj):
def b(self):
pass
class A:
def __init__(self, obj: Obj):
self.obj = obj
class B(A):
def __init__(self, obj: ObjChild):
super().__init__(obj)
def kuku(self):
self.obj.b()
Pycharm warns me about the last line that:
Unresolved attribute reference 'b' for class 'Obj'
But I type-hinted the obj that is being passed in the __init__ to be ObjChild, so it should know statically that self.obj is a ObjChild instance, and would have the b method.
How can I make Pycharm realize that? It messes up my autocompletions in the project.
Make A a Generic:
from typing import Generic, TypeVar
class Obj:
def a(self):
pass
_Obj = TypeVar('_Obj', bound=Obj)
class ObjChild(Obj):
def b(self):
pass
class A(Generic[_Obj]):
def __init__(self, obj: _Obj):
self.obj = obj
class B(A[ObjChild]):
def __init__(self, obj: ObjChild):
super().__init__(obj)
self.obj.b()
Unless you make A a generic, there's no connection between passing an ObjChild to A.__init__ and A.obj being an ObjChild.
I have 2 class (that are defined in two different package).
An A object as a "set" of B objects that all refer to the said A object.
Here is how it looks like :
the a.py :
from b import B
class A():
def __init__(self, data):
self.data = data
self.Bs = {}
def add_B(self, id, data_B):
self.Bs[id] = B(data_B, self)
the b.py :
class B():
def __init__(self, data, a_instance):
self.data = data
self.a = a_instance
so everything works preety good, but I'd like to hint python that the a_instance is indeed a class A object to have autocompletion in visual studio code.
At first i've tried to add from a import A and modify def __init__(self, data, a_instance : A): in the b.py file, but i've obviously got a circular import error
So I've been trying to use the typing package, and so added those lines to the a.py file :
from typing import NewType
A_type = NewType('A_type', A)
But I'm steel getting a circular import error.
Can Anyone explain me what I'm doing wrong ?
thanks for the help
PS: My classes actually have some complex methods and are defined in _a.py (resp. _b.py) and the __init__.py just import the class A and declare the A_type (resp. just import the class B)
Use
typing.TYPE_CHECKING, a variable that's never true at runtime
the string form of a type annotation to refer to a name that is not in scope at runtime:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from a import A
class B:
def __init__(self, data, a_instance: "A"):
...
However, if you can restructure your code in a way that avoids circular imports altogether, all the better.
You could try an abstract class with attributes of A and B, then implement each accordingly.
from collections.abc import abstractmethod, ABCMeta
class ABInerface(metaclass=ABCMeta):
#property
#abstractmethod
def data(self):
pass
#data.setter
#abstractmethod
def data(self, value):
pass
#property
#abstractmethod
def Bs(self):
pass
#Bs.setter
#abstractmethod
def Bs(self, value):
pass
#property
#abstractmethod
def a(self):
pass
#a.setter
#abstractmethod
def a(self, value):
pass
#abstractmethod
def add_B(self, id, data_B):
pass
Then, create each class by extending the Interface meta class.
class B(ABInerface):
def __init__(self, data, a_instance):
self.data = data
self.a = a_instance
class A(ABInerface):
def __init__(self, data):
self.data = data
def add_B(self, id, data_B):
self.Bs[_id] = B(data_B, self)
Was wondering if there was a way to set a class attribute to a specific instance from within the class definition. For example,
class Value:
def __init__(self, x):
self.x = x
# Something like
# half = Value(0.5)
>>> Value.half.x
0.5
>>> Value.half.half.x
0.5
I'm also aware I can easily set it outside the class that seems a bit more bulky and error prone, like this
class Value:
def __init__(self, x):
self.x = x
Value.half = Value(0.5)
>>> Value.half.x
0.5
>>> Value.half.half.x
0.5
No. At the time the body of the class is being evaluated, the class doesn't yet exist. A class statement is a declarative syntax for calling a metaclass:
class Value:
def __init__(self, x):
self.x = x
is roughly equivalent to
def init(self, x):
self.x = x
Value = type('Value', (object,), {'__init__': init})
Your class attribute would have to be a member of the dict passed as the third argument, which has to be fully defined before type is called.
not quite, but you can make a class method that return a new instance of your class in whatever way you want with the classmethod decorator
>>> class Value:
def __init__(self, x):
self.x=x
def __repr__(self):
return f"{type(self).__name__}({self.x})"
#classmethod
def half(cls):
return cls(0.5)
>>> Value(10)
Value(10)
>>> Value.half()
Value(0.5)
>>>
look like in py3.9 you can combine it with the property decorator to accomplish just that, see linked documentation above (but I don't have it at the moment)
Simply, you can't because the class hasn't yet existed. But you can use either metaclass or class decorator to achieve the same goal as the following shows:
#Metaclass
class Meta(type):
def __init__(cls, clsname, clsbases, clsdict):
cls.half = cls(0.5)
class Value(metaclass=Meta):
def __init__(self, x):
self.x = x
#Decorator
def decorator(cls):
cls.half = cls(0.5)
return cls
#decorator
class Value2:
def __init__(self, x):
self.x = x
print(Value.half.half.x)
print(Value.half.x)
print(Value2.half.half.x)
print(Value2.half.x)
I have a class and a sub-class, I'd like to pass the whole of the self of the class to the sub-class. I can pass self over to the new class explicitly easily enough, e.g.
class foo:
def __init__(self, a, b):
self.a = a
self.b = b
self.c = 'foo'
def foo_method(self):
print('a foo method')
class bar(foo):
def __init__(self, foo_object):
self.a = foo_object.a
self.b = foo_object.b
self.c = foo_object.c
def bar_method(self):
print('a bar method')
foo_object = foo(a = 'a', b = 'b')
bar_object = bar(foo_object)
bar_object.a
Is there a more succinct way to pass these over? Something like:
class bar(foo):
def __init__(self, foo_object):
self = self.foo_object
Update:
Thanks https://stackoverflow.com/users/10104112/bastien-antoine, the following solution worked:
class bar(foo):
def __init__(self, foo_object):
self.__dict__ = foo_object.__dict__.copy()
def bar_method(self):
print('a bar method with ' + str(self.c))
Have you tried the copy builtins library?
Otherwise I think you can easily implement your own .copy() method that would copy the values from the old object __dict__ to the new one. Something like this:
class MyObject:
a = None
def set_default_values(self):
self.a = 1
def copy(self, old):
if type(self) == type(old):
self.__dict__ = old.__dict__.copy()
else:
raise TypeError('Wrong type')
if __name__ == "__main__":
obj_1 = MyObject()
print(obj_1.a)
obj_1.set_default_values()
print(obj_1.a)
obj_2 = MyObject()
print(obj_2.a)
obj_2.copy(obj_1)
print(obj_2.a)
Note that I've added a type checking to be sure that you copy attributes that would exist otherwise, but I think simply self.__dict__ = old.__dict__.copy() would work fine, thought you might end up with attributes you might not suppose to have in the new object.
Hope this helps!
I think that you can do that with
class bar(foo):
def __init__(self):
super(bar, self).__init__()
with this code, you ran the init function for the subclass
If I do something like this:
class MyClass(object):
def __init__(self, a=MyClass.f):
self.a = a
#classmethod
def f():
print 'tump drump'
I get the following error:
NameError: name 'MyClass' is not defined
Obviously, I could do this:
class MyClass(object):
def __init__(self, a=None):
if a is None:
self.a = MyClass.f
else:
self.a = a
But is there a more elegant way to use a classmethod as default argument of a class method?
No, there isn't, because the functions are created before the class object is. There is not class to reference here, and the use of a sentinel (like None) is the correct way to do this.
Note that there is no need to use an else suite if you assign to a rather than self.a in the if suite:
class MyClass(object):
def __init__(self, a=None):
if a is None:
a = MyClass.f
self.a = a
or you could use a conditional expression:
class MyClass(object):
def __init__(self, a=None):
self.a = MyClass.f if a is None else a
or even:
class MyClass(object):
def __init__(self, a=None):
self.a = a or MyClass.f
if all you need to support is truthy objects (function objects always are 'true' in a boolean context, for example).