Call class method in another class method - python

In Python 3, how can I call an inherited method in another class method?
class A:
name = 'foo'
def get_name(self):
return self.name
class B(A):
#classmethod
def do_other(cls):
cls.get_name()
in the cls.get_name() it complains that 'Parameter "self" unfilled'.
How can I overcome this without having to change the do_other to a regular method?

You can accomplish this by creating a temporary instance.
This is one way.
class A:
name = 'foo'
def __init__(self):
self.name = A.name
def get_name(self):
return self.name
class B(A):
#classmethod
def do_other(cls):
return B.get_name(cls)
print(B.do_other())
This is the second way
class B(A):
#classmethod
def do_other(cls):
return A().get_name()
Here you can also replace A() with B() since class B is inheriting from class A

You acutally only have to return the cls.get_name(cls)
class A:
name = 'foo'
def get_name(self):
return self.name
class B(A):
#classmethod
def do_other(cls):
return cls.get_name(cls)
print(B.do_other())

Related

How to inherit all class attributes, but one? Python

How to inherit all class 'A' attributes and methods, but 'b()'?
class A:
def __init__(self):
# attributes
pass
#classmethod
def b(cls):
# logic
pass
class B(A):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def b(self):
# nothing
pass
do not use this old method( if there is another way to do it ):
class B(A):
def __init__(self, attributes):
super().__init__(self, attributes)
You can reimplement b() to raise an error:
class A:
def __init__(self):
# attributes
pass
#classmethod
def b(cls):
# logic
pass
class B(A):
def __init__(self, **kwargs):
super().__init__(**kwargs)
#classmethod
def b(cls):
raise TypeError("method b is not supported in class B")
Also, if b() is a classmethod, you should probably override it as a classmethod.
Put that method in a separate class and don't inherit it.
class A:
def __init__(self):
# attributes
pass
class A1:
#classmethod
def b(cls):
# logic
pass
class B(A):
def __init__(self, **kwargs):
super().__init__(**kwargs)
Use multiple inheritance when you want that method.
class C(A,A1):
pass

Most elegant way to define simple abstract attributes

I'm trying to implement an abstract class with attributes and I can't get how to define it simply.
I just want to define the attribute name to constrain child classes to have it but I don't want to copy/paste getters & setters in every classes that inherit my abstract class.
Here are solutions I found but not very elegant in my opinion:
Maybe the most efficient and robust way, but quite ugly and redundant. We have to put 'def a(): pass', in each child class
class AbstractC(ABC):
#property
#abstractmethod
def a(self):
pass
class ConcreteC1(AbstractC):
def __init__(self, name):
self.a = name
def a(self):
pass
class ConcreteC2(AbstractC):
def __init__(self, name):
self.a = name
class ConcreteC3(AbstractC):
def __init__(self, name):
self.poney = name
ConcreteC1('foobar') # ok
ConcreteC2('foobar') # error !
ConcreteC3('foobar') # error !
Quite the same, but uglier imo
class AbstractC(ABC):
#property
#abstractmethod
def a(self):
pass
class ConcreteC1(AbstractC):
a = None
def __init__(self, name):
self.a = name
class ConcreteC2(AbstractC):
def __init__(self, name):
self.a = name
class ConcreteC3(AbstractC):
def __init__(self, name):
self.poney = name
ConcreteC1('foobar') # ok
ConcreteC2('foobar') # error !
ConcreteC3('foobar') # error !
Most compact way, but not robust. No error if 'a' is missing
class AbstractC(ABC):
#abstractmethod
def __init__(self, val):
self.a = val
class ConcreteC1(AbstractC):
def __init__(self, name):
self.a = name
class ConcreteC2(AbstractC):
def __init__(self, name):
self.poney = name
ConcreteC1('foobar') # ok
ConcreteC2('foobar') # no error !
So is there a way to get an elegant, robust and compact abstract class with abstract attribute ? Or am I trying to get something impossible ? I was thinking about something close to that :
class AbstractC(ABC):
#property
#abstractmethod
def a(self):
pass
class ConcreteC(AbstractC):
def __init__(self, name):
self.a = name
If there is no such solution, what is the best one ?
You could misuse namedtuples for fancy inheritance
from collections import namedtuple
BaseAttributes = namedtuple('base', ['attr1', 'attr2'])
print(BaseAttributes('one', 2))
class SomethingElse(BaseAttributes):
def method(self):
return 3
blubb = SomethingElse('A', 5)
blubb.method()
but imho your last proposal(s) makes sense if you raise NotImplementedError, e.g.:
class AbstractC(ABC):
def a(self):
raise NotImplementedError('Implement _a_ method')
class ConcreteC(AbstractC):
def __init__(self, name, *args, **kwargs):
super().__init__(*args, **kwargs)
self.a = name
Maybe this will help. I made a class which inherits from ABC. It defines the method __init_subclass__ that is invoked after a new subclass is created. It does the next: For each abstract property declared, search the same method in the subclass. If it exists (its a function object) convert it to a property and replace it in the subclass dictionary.
from abc import ABC, abstractmethod
class Foo(ABC):
def __init_subclass__(cls):
super().__init_subclass__()
###### This is the new part. I explain it at the end of the answer
for name, value in attrs.items():
if name not in cls.__dict__:
setattr(cls, name, property(lambda *args, **kwargs: value))
######
# Iterate throught all abstract methods on the class
for name in Foo.__abstractmethods__:
absmethod = Foo.__dict__[name]
# Check if the abstract method is a property
if not isinstance(absmethod, property):
continue
# Check if there is a method defined in the subclass with the same name
if name not in cls.__dict__ or not callable(cls.__dict__[name]):
continue
method = cls.__dict__[name]
# If the method is not already a property, we decorate it automatically...
if not isinstance(method, property):
setattr(cls, name, property(method))
#property
#abstractmethod
def a(self):
return 1
Now define a subclass and test it:
class Bar(Foo):
def __init__(self):
pass
def a(self):
return 2
#property
def b(self):
return 3
obj = Bar()
print(obj.a)
print(obj.b)
Output will be:
2
3
The next code will raise an error, because not all abstract methods are implemented:
class Qux(Foo):
pass
EDIT:
Now you can also do:
class Bar(Foo, a=1):
pass
print(Bar().a) # 1
There's still a problem. If i choose the implementation that raise an error, i have to add #property to the method or i can call ConcreteC().a even if a is not set and it will not raise the error:
class AbstractC(ABC):
def a(self):
raise NotImplementedError('Implement _a_ method')
class ConcreteC(AbstractC):
def __init__(self, val):
super().__init__()
self.poney = val
In [3]: ConcreteC('foobar').a
Out[3]: <bound method AbstractC.a of <__main__.ConcreteC object at 0x7f2e1c6b0518>>
But if i add #property i get an error :
class AbstractC(ABC):
#property
def a(self):
raise NotImplementedError('Implement _a_ method')
class ConcreteC(AbstractC):
def __init__(self, val):
super().__init__()
self.a = val
In [4]: ConcreteC('foobar')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-55-587237cb76e5> in <module>
----> 1 ConcreteC('foobar')
~/workspace/draft.py in __init__(self, val)
151 def __init__(self, val):
152 super().__init__()
--> 153 self.a = val
154
155
AttributeError: can't set attribute
EDIT:
Here the solution I chose:
class AbstractC(ABC):
#property
def a(self):
try:
return self._a
except AttributeError:
raise NotImplementedError('Implement _a_ method')
#a.setter
def a(self, val):
self._a = val
class ConcreteC(AbstractC):
def __init__(self, val):
self.a = val
This way I can edit 'a' very simply and if it's not definied, an exception is raised on get. I didn't know that to make a setter work, it must has the same name as the property.
In the end, what I wanted isn't an abstract attribute, but a concrete one in an abstract class.
In [1]: c = ConcreteC('foobar')
In [2]: c.a
Out[2]: 'foobar'
In [3]: c.a = 'poney'
In [4]: c.a
Out[4]: 'poney'

how to modify parent class variable with the child class and use in another child class in python

class A(object):
__A = None
def get_a(self):
return self.__A
def set_a(self, value):
self.__A = value
class B(A):
def method_b(self, value):
self.set_a(value)
class C(A):
def method_c(self)
self.get_a()
Someone can to explain me how can i to catch installed value in method_b inside my 'C' class method?
P.S. In this variant i just getting nothing.
Python isn't Java; you don't need setters & getters here: just access the attributes directly.
There are three problems with your code.
C.method_c() has no return statement, so it returns None.
You are using __ name mangling when that's exactly what you don't want.
In A.set_a() you want to set a class attribute, but your assignment instead creates an instance attribute which shadows the class attribute.
Here's a repaired version.
class A(object):
_A = 'nothing'
def get_a(self):
return self._A
def set_a(self, value):
A._A = value
class B(A):
def method_b(self, value):
self.set_a(value)
class C(A):
def method_c(self):
return self.get_a()
b = B()
c = C()
print(c.method_c())
b.method_b(13)
print(c.method_c())
output
nothing
13
Here's a slightly more Pythonic version:
class A(object):
_A = 'nothing'
class B(A):
def method_b(self, value):
A._A = value
class C(A):
pass
b = B()
c = C()
print(c._A)
b.method_b(13)
print(c._A)

Alternative for inheritance in python

How to save code duplication in the following scenario ?
say Aand B are two classes having a common function(say) name
class A(object):
name = 'foo'
#property
def name(self): # the common function
return self.name
similarly B
class B(object):
name = 'bar'
#property
def name(self):
return self.name
One way would be to make a class from which both of them inherit from, and define name there.
Any good alternatives ?
If you're really determined to avoid inheritance, just define a function outside of either class:
def get_name(object):
return object.name
class A(object):
name = 'foo'
def get_name(self): # the common function
return self.name
class B(A):
pass
In this case B would inherit from A
Is there a reason you can't have B inherit from A?
class B(A):
name = 'bar'
Since you are decorating name with #property, I am assuming you want this to be an instance variable. If you want this to return a more private variable, let's call it _name, you have to do:
class A(object):
def __init__(self):
self._name = 'foo'
#property
def name(self):
return self._name
You can't have both a variable and a function have the same name, since the latter will simply override the former. If you want a base class that takes care of this, it would look like this:
class HasName(object):
def __init__(self, name):
self._name = name
#property
def name(self):
return self._name
class A(HasName):
def __init__(self):
self._name = 'foo'
class B(HasName):
def __init__(self):
self._name = 'bar'
You can also call the constructor in HasName.
Assuming self.name stands in for a more complex method, the easiest way to cut down on duplicated code is to move the function out to the module and have it take an arbitrary object as a parameter. Then, if you still want to tie the method directly to the class, you can add a short method that dispatches to the module function.
def _name(obj):
return obj.name
class A(object):
# ...
#property
def name(self):
return _name(self)
class B(object):
# ...
#property
def name(self):
return _name(self)
Note that this will not work well if A.name and B.name have completely different behaviors. If the _name function starts checking the type of the object given, reconsider whether you really want to abstract that functionality in the first place.

confusion over inherited objects in abstract base class

Im a bit confused about inherited instance variables in ABCs. I have written an example to show my confusion. Class A needs a list which class B inherits but it must be an instance object rather than a class object. However class B also needs its own instance variable local. Can anyone set me straight?
#!python
from abc import ABCMeta, abstractmethod, abstractproperty
import unittest
class A(object):
__metaclass__ = ABCMeta
_internal = ['initialized']
#property
def internal(self):
return self._internal
def get_a(self):
return self._internal
#abstractmethod
def set_a(self, value):
pass
class B(A):
def __init__(self):
self.local = 'OK'
def get_local(self):
return self.local
def set_a(self, value):
self._internal.append(value)
class TestCase(unittest.TestCase):
def test_implementation(self):
self.assertEqual(['initialized'], B().get_a() ) # this passes but for wrong reason
b_used = B().set_a('used')
b_unused = B()
print "b_used.get_a() should return ['initialized','used']"
print "b_unused.get_a() should return ['initialized']"
print "b_used.get_local() should equal b_unused.get_local() = 'OK'"
self.assertEqual(['initialized'], b_unused.get_a()) # >> fails with ['initialized'] =! ['initialized', 'used']
self.assertNotEqual(b_unused.get_a(), b_used.get_a())
if __name__ == "__main__":
unittest.main()
The problem is that _internal is a class obj of class A. I need it to be an instance object of class B.
Thanks In advance
You should initialize instance attributes in __init__() and call the base class __init__() in B:
class A(object):
__metaclass__ = ABCMeta
def __init__(self):
self._internal = ['initialized']
...
class B(A):
def __init__(self):
A.__init__(self)
self.local = 'OK'
...
You should also fix your unit test:
class TestCase(unittest.TestCase):
def test_implementation(self):
self.assertEqual(['initialized'], B().get_a() ) # this passes but for wrong reason
b_used = B()
b_used.set_a('used')
b_unused = B()
...
Instance attributes should be defined in a method, eg __init__, by setting them on self.

Categories

Resources