Python Property in Abstract Base can become method upon inherit - python

from abc import ABC, abstractmethod
class BasketballPlayer(ABC):
move1 = 'pass'
move2 = 'dribble'
move3 = 'shoot'
#property
#abstractmethod
def name(self):
pass
class Player2(BasketballPlayer):
def name(self):
pass
y = Player2()
I expect an error on the line y = Player2() because the name is suddenly declared as a method instead of a property. Is this a bug? or something I did wrong?

Equally Python won't care if you implement #abstractmethod as class variable. I am not sure what is logic behind this, perhaps good question to ask on: https://github.com/python/cpython/issues
from abc import ABC, abstractmethod, abstractproperty
class MyAbstractClass(ABC):
#property
#abstractmethod
def name(self):
pass
#abstractmethod
def run(self):
pass
class Child(MyAbstractClass):
name = "foo"
run = "foo"
c = Child()
print(c.name)
I found similar question with answer here: enforcement for abstract properties in python3

Related

Calling class methods from class body

I have the code something like:
class ClassPrintable:
#classmethod
def print_class(cls):
print(cls)
I would like to be able to derive classes from this, and furthermore call the class methods inline from the class body, eg.
class MyClass(ClassPrintable):
print_class()
Unfortunately this doesn't work, however this does:
class MyClass(ClassPrintable):
ClassPrintable.print_class()
Unfortunately, of course, it prints the class for ClassPrintable rather than MyClass
The obvious solution, doesn't work, eg.
class MyClass(ClassPrintable):
MyClass.print_class()
Python complains it can't find MyClass! with a NameError: name 'MyClass' is not defined
How can I access MyClass's class method from within the body of its definition? I would prefer not to use dynanic metaprogramming but I will if I have to.
You cannot invoke anything on the class before it exists which is only after the class definition (note that method bodies aren't evaluated at class definition time). In Python >= 3.6, you can do the following, using the __init_subclass__ hook:
class ClassPrintable:
#classmethod
def print_class(cls):
print(cls)
#classmethod
def __init_subclass__(cls):
cls.print_class()
class MyClass(ClassPrintable):
pass
Alright I figured it out with small amount of metaprogramming. Whoever thought of __init_subclass__ is a genius. If anyone can see anything drastically wrong with this let me know.
import copy
class Model:
def __init__(self, name, default):
self.model_name = name
self.model_default = default
self.observers = []
class Models():
model_dictionary = {}
def __init_subclass__(cls, models=[]):
setattr(cls, "model_dictionary", {})
for model in models:
cls.model_dictionary[model[0]] = Model(model[0], model[1])
for c in cls.__bases__:
cls.add_base_models(c)
#classmethod
def add_base_models(cls, base):
if hasattr(base, "model_dictionary"):
for model in base.model_dictionary.values():
cls.model_dictionary[model.model_name] = copy.copy(base.model_dictionary[model.model_name])
for c in base.__bases__:
cls.add_base_models(c)
#classmethod
def listen(cls, name, closure):
cls.model_dictionary[name].observers.append(closure)
def __init__(self):
for model in self.model_dictionary.values():
super().__setattr__(model.model_name, model.model_default)
def __setattr__(self, name, value):
if name in self.__class__.model_dictionary.keys():
orig_value = getattr(self, name)
if value != orig_value:
for observer in self.model_dictionary[name].observers:
observer(self, value)
super().__setattr__(name, value)
else:
super().__setattr__(name, value)
Sample use of the code:
class Mouse(Models, models=[("x", 100), ("y", 200), ("visible", True)]):
pass
class SpecialMouse(Mouse, models=[("anger_level", "hostile")]):
pass
mouse = SpecialMouse()
mouse.listen("anger_level", lambda mouse, value : print(value))
mouse.anger_level = "cold!"
mouse.anger_level = "warm"
mouse.anger_level = "warm"
mouse.anger_level = "furious"
Prints out:
cold!
warm
furious

What is the pythonic way to overload class variables(properties)?

Hello!
I need each child class to has own set of constants. I've found a "proper" way with properties and overloading setter methods, but:
I need to define constructor in child classes (which I don't need) and assign values in constructor;
Every instance of class will have copy of this constants in memory (senseless resource consumption);
It looks weird when you define setter, getter and property at all just to use it as constant.
I've done something like this:
class BaseClass:
def get_a(self):
raise NotImplementedError("Oooops")
def get_b(self):
raise NotImplementedError("Oooops")
class FirstClass(BaseClass):
def get_a(self):
return "a"
def get_b(self):
return "b"
class SecondClass(BaseClass)
def get_a(self):
return "A"
def get_b(self):
return "B"
class SomeClass:
def some_method(self, class_param):
return "{}-{}".format(class_param.get_a, class_param.get_b)
This method also doesn't solve problems of method with properties (except last), just more compact. There's other way, which I find not good:
class BaseClass:
pass
class FirstClass(BaseClass):
A_CONST = "a"
B_CONST = "b"
class SecondClass(BaseClass)
A_CONST = "A"
B_CONST = "B"
class SomeClass:
def some_method(self, class_param):
return "{}-{}".format(class_param.A_CONST, class_param.B_CONST)
In fact, it solve all problems and pretty compact, BUT it violates rule of inheritance (isn't it?).
Question:
What is the proper way to do this?
P.S. Provided code is simplified example, base class contains methods which I use in child class, please don't write me that base class is useless here.
If you want your base class to indicate that it needs to be subclassed with certain attributes, you can make it an abstract base class.
from abc import ABC, abstractmethod
class Base(ABC):
#property
#abstractmethod
def a(self):
raise NotImplementedError
#property
#abstractmethod
def b(self):
raise NotImplementedError
You will then not be allowed to instantiate Base or its subclasses unless they override the abstract methods. You can do either
class First(Base):
a = 1
b = 2
to assign class attributes with those names, or
class Second(Base):
#Base.a.getter
def a(self):
return 3
#Base.b.getter
def b(self):
return 4
The benefit of the second approach is that it will raise an error if you try to assign to the property
Second().a = 5 # AttributeError
your second version looks fine to me… each language has their own conventions around what a "class" or "object" means, and this looks reasonably "Pythonic"
one minor comment about the first version, is that Python doesn't care about "overloading", you don't need to include:
class BaseClass:
def get_a(self):
raise NotImplementedError("Oooops")
at all, i.e. it's fine to have:
class BaseClass:
pass
as well in your first version.
another potentially useful tool here is the property decorator, e.g:
class FirstClass(BaseClass):
#property
def a(self):
return "a"
print(FirstClass().a)
would output "a"
If the key_name : [A_CONST, B_CONST] remains same for child classes, super() will take care of all your concerns (1., 2., 3.).
A 'pythonic' solution would include, to remove duplication's, of any, setter and getter in child classes and let BaseClass() handle these common-tasks.
class BaseClass(object):
def __init__(self, a, b):
self._a_const = a
self._b_const = b
#property
def A_CONST(self):
return self._a_const
#property
def B_CONST(self):
return self._b_const
class FirstClass(BaseClass):
def __init__(self, _aconst, _bconst):
# Let Base class object hold my constants but FirstClass Constructor
# is setting the value. Look SecondClass
super(FirstClass, self).__init__(_aconst, _bconst)
class SecondClass(BaseClass):
def __init__(self, _aconst, _bconst):
# Magic happens here
super(SecondClass, self).__init__(_aconst, _bconst)
class SomeClass():
def some_method(self, class_param):
return "{}-{}".format(class_param.A_CONST, class_param.B_CONST)
firstobj = FirstClass("a", "b")
secondobj = SecondClass("A", "B")
print(SomeClass().some_method(firstobj))
print(SomeClass().some_method(secondobj))

Identify the superclass that defines a class-level variable

In the case of multiple inheritance in python, is there a way to identify which super class a class-level variable is obtained from?
All attempts I tried to google are overwhelmingly about How to get the attribute not find out where it came from:
https://www.google.com/search?q=pythin+which+super+class+defines+attr
https://www.google.com/search?q=python+which+super+class+has+attribute&oq=python+which+super+class+has+attr
https://www.google.com/search?q=python+which+super+class+attribute+obtained+from
I suppose I can manually step through the MRO using inspect.getmro(cls). But I couldn't find any more elegant solutions. Just wondering if anyone knows of one.
EDIT
For a concrete example:
class Super1(object):
__class_attribute__ = "Foo"
class Super2(object):
pass
class Derived(Super1, Super2):
pass
d = Derived()
parent_cls = some_function_to_get_defining_class(d.__class_attribute__) # <-- should return `Super1`
The __qualname__ attribute gives an indication from which class a method was inherited. However, this only returns a string, not the superclass itself. If you need to the superclass for metaprogramming, I think you are going to have to dig into the MRO.
class A:
def a(self):
return 1
def b(self):
return 2
class B:
def b(self):
return 2.5
def c(self):
return 3
class C(A,B):
pass
Using:
C.b.__qualname__
# returns:
'A.b'
However, this does not apply when using abstract methods to define an interface, since the method has to be overwritten.
from abc import abstractmethod
class A:
def a(self):
return 1
#abstractmethod
def b(self):
pass
class C(A):
def b(self):
return 100
C.b.__qualname__
# returns:
'C.b'

Python can't set attribute on abstractproperty

I have the following snippet:
from abc import abstractproperty
class Base(object):
#abstractproperty
def foo(self):
print 'wat'
class C(Base):
def __init__(self):
self.foo = 'test'
self.bar = 'test2'
c = C()
When I execute it, it gives the stacktrace:
in __init__ AttributeError: can't set attribute
on the line self.foo = 'test'.
Anyone know why this is happening?
First of all, note that you forgot to use ABCMeta as your metaclass. Change your code to:
For Python 2.x:
from abc import ABCMeta
class Base(object):
__metaclass__ = ABCMeta
...
For Python 3.x, any of the following:
from abc import ABC, ABCMeta
class Base(ABC):
...
class Base(metaclass=ABCMeta):
...
And you'll see this nice error:
Traceback (most recent call last):
File "a.py", line 14, in <module>
c = C()
TypeError: Can't instantiate abstract class C with abstract methods foo
It's telling you that you need a concrete implementation for your abstract property.
But that's not enough: to properly have self.foo = 'test' working, you need to implement a concrete setter for your property.
In the end, your code should look like this:
from abc import ABCMeta, abstractproperty
class Base(object):
__metaclass__ = ABCMeta
#abstractproperty
def foo(self):
print 'wat'
class C(Base):
#property
def foo(self):
# do something
#foo.setter
def foo(self, value):
# do something else
def __init__(self):
self.foo = 'test'
self.bar = 'test2'
c = C()
Remember that you can use super() in your concrete property code to use the code from the abstract property.
Note: that since Python 3.3, #abstractproperty has been deprecated. Instead, you should use a combination of #property and #abstractmethod:
class Base(ABC):
#property
#abstractmethod
def foo(self):
...
Your code defines a read-only abstractproperty. See docs on ABC. In addition, you did not set ABCMeta as meta class, which is obligatory. Furthermore, an abstractproperty is abstract which means that it has to be overwritten in the child class.
If you want to create a read-write abstractproperty, go with something like this:
from abc import ABCMeta, abstractproperty
class Base(object):
__metaclass__ = ABCMeta # This is required.
def getfoo(self):
pass # Getter for preprocessing when property 'foo' is requested
def setfoo(self, thefoo):
pass # Setter for preprocessing when property 'foo' is set
foo = abstractproperty(getfoo, setfoo)
class C(Base):
foo = '' # Overwrite abstract property
def __init__(self):
self.foo = 'test'
self.bar = 'test2'
Using the code above, you can instantiate your class C and set its property foo:
c = C()
print(c.foo)
>>> test

Implementing Abstract Properties in Python

What's the best, most Pythonic, way to deal with abstract properties in Python? If I want to implement a base class which has a mixture of abstract properties, and concrete methods, I can do so similar to the following.
class BaseClass(object):
__metaclass__ = ABCMeta
#abstractmethod
def property1(self):
pass
#abstractmethod
def property2(self):
pass
#abstractmethod
def property3(self):
pass
#abstractmethod
def abstract_method(self):
pass
def concrete_method(self):
return self.property1 + self.property2
However, when I then go to implement the inheriting class I need to implement each of those properties as getter method for a private property.
class Klass(BaseClass):
def __init__(property1, property2, property3):
self.__property1 = property1
self.__property2 = property2
self.__property3 = property3
#property
def property1(self):
return self.__property1
#property
def property2(self):
return self.__property2
#property
def property3(self):
return self.__property3
Which seems both unnecessarily verbose, and makes the code more obscure than it needs to be.
I don't love the idea of implementing things concretely and raising a NotImplementedErrorif the inheriting class doesn't implement it's own version.
Is there a better way to do this?
You are not required to implement properties as properties. All you need is for the name to exist on the class. So the following, using regular attributes, would work:
class Klass(BaseClass):
property1 = None
property2 = None
property3 = None
def __init__(property1, property2, property3):
self.property1 = property1
self.property2 = property2
self.property3 = property3
def abstract_method(self):
# implementation for the abstract method
Note that there is a abstractproperty decorator that'd better document that you want to use those names as simple values, not methods to call:
class BaseClass(object):
__metaclass__ = ABCMeta
#abstractproperty
def property1(self):
pass
#abstractproperty
def property2(self):
pass
#abstractproperty
def property3(self):
pass
#abstractmethod
def abstract_method(self):
pass
def concrete_method(self):
return self.property1 + self.property2

Categories