Use classmethod as default argument - python

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).

Related

Subclass method not getting resolved

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.

Can you make a class attribute a specific instance of that class within the class definition?

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)

How do I pass a default method as a parameter in Python

Here is my code:
class class1():
def __init__(self):
pass
def method1(self):
pass
def independent_method(class1_instance, param2='method1'):
return class1_instance.param2()
c = class1()
independent_method(c)
I get this error:
'class1' object has no attribute 'param2'
How do I get around this problem? I've tried different variations.
Use __getattribute__ to fetch the method by its given name in param2:
class class1():
def __init__(self):
pass
def method1(self):
pass
def independent_method(class1_instance, param2='method1'):
return class1_instance.__getattribute__(param2)()
c = class1()
independent_method(c)
You can also use getattr... that's actually a better way to do so.
class class1():
def __init__(self):
pass
def method1(self):
pass
def independent_method(class1_instance, param2='method1'):
return getattr(class1_instance, param2)()
c = class1()
independent_method(c)

Python: pass whole self of class to init of new class

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

Access all variables by their names of specific object in class

I have two python classes:
class A:
def __init__(self, param1):
self.param1 = param1
class B:
def __init__(self, a):
self.a = a
Now I have an instance of B and need to access param1, I can just write b.a.param1. But the goal is to omit the 'a' part, so access this param with b.param1. I could add property to class B, but I am searching for generic solution - when A class has a lot variables. Is it possible? And would it be clean solution?
This is not the most elegant option and probably a bad practice but you can copy all the attributes of a to be attributes of b using getattr and setattr:
import inspect
class A:
def __init__(self, param1):
self.param1 = param1
class B:
def __init__(self, a):
self.a = a
variables = [i for i in dir(a) if not inspect.ismethod(i) and i[:2] != '__']
for var in variables:
setattr(self, var, getattr(a, var))
This way you can access a's attributes directly:
a = A(1)
b = B(a)
b.param1
which will return
1

Categories