Python magic method for missing class method? - python-3.x

I need for an abstract class to be able to handle missing class method. I find out how to do this for instance method with __getattr__ but it's not working with class method. Is it even possible ?
I've got something like this :
class Container:
_definitions = {'class_a': 'example.class_a.ClassA',
'class_b': 'example.class_b.ClassB'}
#classmethod
def get(cls, def_id):
# import dynamicaly
parts = cls._definitions[def_id].split(".")
module_name = ".".join(parts[:-1])
class_name = parts[-1]
__import__(module_name)
return class_name
class AbstractClass:
#classmethod
def __getattr__(cls, name, *args):
def missing_method():
result = re.search("^(?P<class_id>[a-z0-9._-]*)$", name)
if result:
return Container.get(result.group('class_id'))
raise RuntimeError("class method '{}' missing from class".format(name))
return missing_method
class ClassA(AbstractClass):
#classmethod
def method(cls):
classb = cls.class_a()
classb.method()
class ClassB(AbstractClass):
#classmethod
def method(cls):
print('Hello world')
Each class in my Container must extend AbstractClass to be able to magically call any class in it, like I do un classA.method().
It's working if I do not use class method, but my purpose is that every class into my container can not be instantiate cause it will be useless for my needs. It's a kind of Singleton pattern.
Is it more understandable ?

Related

Python dynamic inheritance

I wonder if I can somehow do this scenario. I have a Core class that will inherit from some dynamic class, and then pass it and inherits it. maybe with __init__().super(), not sure. can someone help?
file1:
class Core(T):
# some logic
...
file2:
class CustomClass(Core(Logic1)):
def execute():
return self.apply() condition else []
file3:
class CustomClass(Core(Logic2)):
def execute():
return self.apply()
I didn't fully understand your purpose, but I suggest you try to use generics with composition:
from typing import TypeVar, Generic,
T = TypeVar('T')
class Core(Generic[T]):
logic: T
def __init__(self, logic: T, ...):
self.logic = logic
...
class CustomClass(Core[Logic1]):
def __init__(self):
super().__init__(Logic1(...))
def execute():
return self.apply()
....

Inherit private attribute from abstract class python3

I have an abstract class that i will use as template to implement different kind of subclasses.
I want to define some attributes/methods that are mandatory to all subclasses. For example
class BlockModel(ABC):
def __init__(self, position):
self.__position = position
self.__lives = None
self.__hitbox = None
#property
def lives(self):
return self.__lives
#lives.setter
def lives(self, lives):
self.__lives = lives
#property
def hitbox(self):
return self.__hitbox
#hitbox.setter
def hitbox(self, hitbox):
self.__hitbox = hitbox
#abstractmethod
def method1(self)
#some abstract methods
#abstractmethod
def method2(self)
#some abstract methods
When i create a subclass, for example
class Block1(BlockModel):
def __init__(self,position_):
super().__init__(position_)
self.__lives=1
self.__hitbox = pygame.Rect(self.__position['x'],
self.__position['y'],
5,
5)
#Implement abstract methods
The second class doesn't inherit the attributes __position, __lives, __hitbox, but the public ones without the underscores (i know that there are no real private attributes/methods in python). There s a way too keep them private(with underscores) in the subclass too?

Better way than pass the same argument among classes in Python

I have a question related to OOP but it should be implemented in Python.
I have a file user_inputs.py with all the user parameters.
In my main file, I have a function that is called first. This function is responsible to read all the user parameters and return a dictionary that will be used in the rest of the program.
My question is: what is the cleanest way to pass the user_parameters dictionary to all classes? I did it in 2 ways:
Method 1)
def read_user_parameters():
# code to open and read all parameters etc.
return user_parameters # returns a dictionary with all the user parameters
Class A():
def __init__(self, user_parameters):
self.user_parameters = user_parameters
Class B():
def __init__(self, user_parameters):
self.user_parameters = user_parameters
user_parameters = read_user_parameters()
object_A = A(user_parameters)
object_B = B(user_parameters)
I don't like this way because I have dozens of classes that need to pass this argument. So I thought to create a parent class with the user parameters:
Method 2)
Class User_parameters():
def __init__(self, user_parameters):
def read_user_parameters():
# code to open and read all parameters etc.
return user_parameters
Class A(User_parameters):
__init__(self, user_parameters):
super().__init__()
# self.user_parameters comes from the parent class now
Class B(User_parameters):
__init__(self, user_parameters):
super().__init__()
# self.user_parameters comes from the parent class now
object_A = A()
object_B = B()
I prefer method 2, however, when super() is initialized from Class A and Class B the function read_user_parameters() that reads the file will be called twice (multiply this by dozens of times). Is there a better solution than method 1 in which I call read_user_parameters() only once but doesn't need to pass the argument for all classes?
Thank you for your time.
Why not just have a single UserParameters class and two objects of the same class (Also class nameds are supposed to be camel-cases, not snake-cased)
#Single class for user parameters
class UserParameters:
def __init__(self, user_parameters):
self.user_parameters = user_parameters
def read_user_parameters(self):
# code to open and read all parameters etc.
return self.user_parameters
#Two objects
object_A = UserParameters("<params>")
object_B = UserParameters("<params>")

Python Is it ok that an attribute only exists in child/concrete classes [duplicate]

What's the best practice to define an abstract instance attribute, but not as a property?
I would like to write something like:
class AbstractFoo(metaclass=ABCMeta):
#property
#abstractmethod
def bar(self):
pass
class Foo(AbstractFoo):
def __init__(self):
self.bar = 3
Instead of:
class Foo(AbstractFoo):
def __init__(self):
self._bar = 3
#property
def bar(self):
return self._bar
#bar.setter
def setbar(self, bar):
self._bar = bar
#bar.deleter
def delbar(self):
del self._bar
Properties are handy, but for simple attribute requiring no computation they are an overkill. This is especially important for abstract classes which will be subclassed and implemented by the user (I don't want to force someone to use #property when he just could have written self.foo = foo in the __init__).
Abstract attributes in Python question proposes as only answer to use #property and #abstractmethod: it doesn't answer my question.
The ActiveState recipe for an abstract class attribute via AbstractAttribute may be the right way, but I am not sure. It also only works with class attributes and not instance attributes.
A possibly a bit better solution compared to the accepted answer:
from better_abc import ABCMeta, abstract_attribute # see below
class AbstractFoo(metaclass=ABCMeta):
#abstract_attribute
def bar(self):
pass
class Foo(AbstractFoo):
def __init__(self):
self.bar = 3
class BadFoo(AbstractFoo):
def __init__(self):
pass
It will behave like this:
Foo() # ok
BadFoo() # will raise: NotImplementedError: Can't instantiate abstract class BadFoo
# with abstract attributes: bar
This answer uses same approach as the accepted answer, but integrates well with built-in ABC and does not require boilerplate of check_bar() helpers.
Here is the better_abc.py content:
from abc import ABCMeta as NativeABCMeta
class DummyAttribute:
pass
def abstract_attribute(obj=None):
if obj is None:
obj = DummyAttribute()
obj.__is_abstract_attribute__ = True
return obj
class ABCMeta(NativeABCMeta):
def __call__(cls, *args, **kwargs):
instance = NativeABCMeta.__call__(cls, *args, **kwargs)
abstract_attributes = {
name
for name in dir(instance)
if getattr(getattr(instance, name), '__is_abstract_attribute__', False)
}
if abstract_attributes:
raise NotImplementedError(
"Can't instantiate abstract class {} with"
" abstract attributes: {}".format(
cls.__name__,
', '.join(abstract_attributes)
)
)
return instance
The nice thing is that you can do:
class AbstractFoo(metaclass=ABCMeta):
bar = abstract_attribute()
and it will work same as above.
Also one can use:
class ABC(ABCMeta):
pass
to define custom ABC helper. PS. I consider this code to be CC0.
This could be improved by using AST parser to raise earlier (on class declaration) by scanning the __init__ code, but it seems to be an overkill for now (unless someone is willing to implement).
2021: typing support
You can use:
from typing import cast, Any, Callable, TypeVar
R = TypeVar('R')
def abstract_attribute(obj: Callable[[Any], R] = None) -> R:
_obj = cast(Any, obj)
if obj is None:
_obj = DummyAttribute()
_obj.__is_abstract_attribute__ = True
return cast(R, _obj)
which will let mypy highlight some typing issues
class AbstractFooTyped(metaclass=ABCMeta):
#abstract_attribute
def bar(self) -> int:
pass
class FooTyped(AbstractFooTyped):
def __init__(self):
# skipping assignment (which is required!) to demonstrate
# that it works independent of when the assignment is made
pass
f_typed = FooTyped()
_ = f_typed.bar + 'test' # Mypy: Unsupported operand types for + ("int" and "str")
FooTyped.bar = 'test' # Mypy: Incompatible types in assignment (expression has type "str", variable has type "int")
FooTyped.bar + 'test' # Mypy: Unsupported operand types for + ("int" and "str")
and for the shorthand notation, as suggested by #SMiller in the comments:
class AbstractFooTypedShorthand(metaclass=ABCMeta):
bar: int = abstract_attribute()
AbstractFooTypedShorthand.bar += 'test' # Mypy: Unsupported operand types for + ("int" and "str")
Just because you define it as an abstractproperty on the abstract base class doesn't mean you have to make a property on the subclass.
e.g. you can:
In [1]: from abc import ABCMeta, abstractproperty
In [2]: class X(metaclass=ABCMeta):
...: #abstractproperty
...: def required(self):
...: raise NotImplementedError
...:
In [3]: class Y(X):
...: required = True
...:
In [4]: Y()
Out[4]: <__main__.Y at 0x10ae0d390>
If you want to initialise the value in __init__ you can do this:
In [5]: class Z(X):
...: required = None
...: def __init__(self, value):
...: self.required = value
...:
In [6]: Z(value=3)
Out[6]: <__main__.Z at 0x10ae15a20>
Since Python 3.3 abstractproperty is deprecated. So Python 3 users should use the following instead:
from abc import ABCMeta, abstractmethod
class X(metaclass=ABCMeta):
#property
#abstractmethod
def required(self):
raise NotImplementedError
If you really want to enforce that a subclass define a given attribute, you can use metaclasses:
class AbstractFooMeta(type):
def __call__(cls, *args, **kwargs):
"""Called when you call Foo(*args, **kwargs) """
obj = type.__call__(cls, *args, **kwargs)
obj.check_bar()
return obj
class AbstractFoo(object):
__metaclass__ = AbstractFooMeta
bar = None
def check_bar(self):
if self.bar is None:
raise NotImplementedError('Subclasses must define bar')
class GoodFoo(AbstractFoo):
def __init__(self):
self.bar = 3
class BadFoo(AbstractFoo):
def __init__(self):
pass
Basically the meta class redefine __call__ to make sure check_bar is called after the init on an instance.
GoodFoo()  # ok
BadFoo ()  # yield NotImplementedError
As Anentropic said, you don't have to implement an abstractproperty as another property.
However, one thing all answers seem to neglect is Python's member slots (the __slots__ class attribute). Users of your ABCs required to implement abstract properties could simply define them within __slots__ if all that's needed is a data attribute.
So with something like,
class AbstractFoo(abc.ABC):
__slots__ = ()
bar = abc.abstractproperty()
Users can define sub-classes simply like,
class Foo(AbstractFoo):
__slots__ = 'bar', # the only requirement
# define Foo as desired
def __init__(self):
self.bar = ...
Here, Foo.bar behaves like a regular instance attribute, which it is, just implemented differently. This is simple, efficient, and avoids the #property boilerplate that you described.
This works whether or not ABCs define __slots__ at their class' bodies. However, going with __slots__ all the way not only saves memory and provides faster attribute accesses but also gives a meaningful descriptor instead of having intermediates (e.g. bar = None or similar) in sub-classes.1
A few answers suggest doing the "abstract" attribute check after instantiation (i.e. at the meta-class __call__() method) but I find that not only wasteful but also potentially inefficient as the initialization step could be a time-consuming one.
In short, what's required for sub-classes of ABCs is to override the relevant descriptor (be it a property or a method), it doesn't matter how, and documenting to your users that it's possible to use __slots__ as implementation for abstract properties seems to me as the more adequate approach.
1 In any case, at the very least, ABCs should always define an empty __slots__ class attribute because otherwise sub-classes are forced to have __dict__ (dynamic attribute access) and __weakref__ (weak reference support) when instantiated. See the abc or collections.abc modules for examples of this being the case within the standard library.
The problem isn't what, but when:
from abc import ABCMeta, abstractmethod
class AbstractFoo(metaclass=ABCMeta):
#abstractmethod
def bar():
pass
class Foo(AbstractFoo):
bar = object()
isinstance(Foo(), AbstractFoo)
#>>> True
It doesn't matter that bar isn't a method! The problem is that __subclasshook__, the method of doing the check, is a classmethod, so only cares whether the class, not the instance, has the attribute.
I suggest you just don't force this, as it's a hard problem. The alternative is forcing them to predefine the attribute, but that just leaves around dummy attributes that just silence errors.
I've searched around for this for awhile but didn't see anything I like. As you probably know if you do:
class AbstractFoo(object):
#property
def bar(self):
raise NotImplementedError(
"Subclasses of AbstractFoo must set an instance attribute "
"self._bar in it's __init__ method")
class Foo(AbstractFoo):
def __init__(self):
self.bar = "bar"
f = Foo()
You get an AttributeError: can't set attribute which is annoying.
To get around this you can do:
class AbstractFoo(object):
#property
def bar(self):
try:
return self._bar
except AttributeError:
raise NotImplementedError(
"Subclasses of AbstractFoo must set an instance attribute "
"self._bar in it's __init__ method")
class OkFoo(AbstractFoo):
def __init__(self):
self._bar = 3
class BadFoo(AbstractFoo):
pass
a = OkFoo()
b = BadFoo()
print a.bar
print b.bar # raises a NotImplementedError
This avoids the AttributeError: can't set attribute but if you just leave off the abstract property all together:
class AbstractFoo(object):
pass
class Foo(AbstractFoo):
pass
f = Foo()
f.bar
You get an AttributeError: 'Foo' object has no attribute 'bar' which is arguably almost as good as the NotImplementedError. So really my solution is just trading one error message from another .. and you have to use self._bar rather than self.bar in the init.
Following https://docs.python.org/2/library/abc.html you could do something like this in Python 2.7:
from abc import ABCMeta, abstractproperty
class Test(object):
__metaclass__ = ABCMeta
#abstractproperty
def test(self): yield None
def get_test(self):
return self.test
class TestChild(Test):
test = None
def __init__(self, var):
self.test = var
a = TestChild('test')
print(a.get_test())

How to modify/override inherited class function in python?

I have the exact function name say_hello in both parent and inherited class. I want to specify the parameter name in the Kitten class but allows for user to specify the parameter in Cat class.
Is there a way to avoid the needs to repeat the line return ('Hello '+name) in the say_hello function in Kitten class?
Currently:
class Cat:
def __init__(self):
pass
def say_hello(name):
return ('Hello '+name)
class Kitten(Cat):
def __init__(self):
super().__init__()
def say_hello(name='Thomas'):
return ('Hello '+name)
x = Cat
print (x.say_hello("Sally"))
y = Kitten
print (y.say_hello())
Ideally:
class Cat:
def __init__(self):
pass
def say_hello(name):
return ('Hello '+name)
class Kitten(Cat):
def __init__(self):
super().__init__()
def say_hello():
return super().say_hello(name='Thomas') # Something like this, so this portion of the code doesn't need to repeat completely
The say_hello method should include self as the first parameter so that it can use the super() function to access the parent's say_hello method. You should also instantiate a class by calling it with parentheses:
class Cat:
def say_hello(self, name):
return 'Hello ' + name
class Kitten(Cat):
def say_hello(self, name='Thomas'):
return super().say_hello(name)
x = Cat()
print(x.say_hello("Sally"))
y = Kitten()
print(y.say_hello())
This outputs:
Hello Sally
Hello Thomas
i think you might miss a point about classes here which is crucial to what you want to achieve.
Let's first define a class Cat but take a close look at the differences to yours.
class Cat:
def __init__(self, name):
self.name = name
def say_hello(self):
return self.name
And now see the magic and i explain it later :
cat = Cat("Sally")
print(cat.say_hello())
>>> "Sally"
What happened in comparison to your code ?
First, your __init__ method does not take an argument. My version does and it is name. Furthermore i asign it to an instance variable self.name .
With this asignment i can access the name provided at instantiation in EVERY method which has self as it's first argument. What is instantiation ?
Well in your code you called x = Cat. With this x is an object of the class but not an Instance. You need to instantiate your class.
By doing so in my code cat = Cat("Sally") what python does is call the __init__ method of the class and return an Instance of it with everythin set/done as specified in the __init__method.
In my case i set self.name = name which makes , in the example, the value of self.name to "Sally".
So whenever i call self.name in a method , which includes self in it's parameters it becomes, after instantiation, "Sally".
Now the class Cat should be clear.
What to do now ?
Simply inherit from this class !
See:
class Cat:
def __init__(self, name):
self.name = name
def say_hello(self):
return self.name
class Kitten(Cat):
def __init__(self, name):
# This is done for convenience
super().__init__(name)
With this a Kitten-, same as a Cat-instance has a name and inherits the say_hello() mehtod to say it's name .
I hope this helps.

Resources