Is there a way to use __getattr__ as Python classmethod - python-3.x

getattr works pretty well with instance of class:
class Person:
def __getattr__(self, name):
print(name)
p = Person()
p.john
Output:
john
but it does not work as classmethod:
class Person:
#classmethod
def __getattr__(cls, name):
print(name)
Person.john
Output:
AttributeError: type object 'Person' has no attribute 'john'
Is there a way to use getattr for class directly ?

__getattr__, like most special methods, is only looked up on the class of the instance (bypassing the instance itself). Since you're trying to call it on the class itself, it has to be defined on the class that class Person is implementing. By default that's type (the common root for all class types).
If you really need to do this (I suspect an XY problem) you can do this with a metaclass (the class of the class itself):
class PersonMeta(type):
def __getattr__(cls, name):
print(cls, name)
class Person(metaclass=PersonMeta):
def __getattr__(self, name):
return getattr(type(self), name) # Delegate to metaclass implementation
Person.john
Person().john
Try it online!
Note that you need to implement a delegating version on the class itself to ensure it delegates back to the metaclass's implementation (assuming you want to be able to have it function on instances of the class, not just on the class itself). The metaclass itself could dynamically attach such a method to the class at construction time (in __prepare__ or __new__) if all instances of the metaclass's classes should exhibit this delegation behavior.

Related

TypeError: Can't instantiate abstract class <...> with abstract methods - when subclassing another class that implements the abstract method [duplicate]

So, I think the code probably explains what I'm trying to do better than I can in words, so here goes:
import abc
class foo(object):
__metaclass__ = abc.ABCMeta
#abc.abstractmethod
def bar(self):
pass
class bar_for_foo_mixin(object):
def bar(self):
print "This should satisfy the abstract method requirement"
class myfoo(foo, bar_for_foo_mixin):
def __init__(self):
print "myfoo __init__ called"
self.bar()
obj = myfoo()
The result:
TypeError: Can't instantiate abstract class myfoo with abstract methods bar
I'm trying to get the mixin class to satisfy the requirements of the abstract/interface class. What am I missing?
Shouldn't the inheritance be the other way round? In the MRO foo currently comes before bar_for_foo_mixin, and then rightfully complains. With class myfoo(bar_for_foo_mixin, foo) it should work.
And I am not sure if your class design is the right way to do it. Since you use a mixin for implementing bar it might be better not to derive from foo and just register it with the 'foo' class (i.e. foo.register(myfoo)). But this is just my gut feeling.
For completeness, here is the documentation for ABCs.
i think (tested in similar case) that reversing the baseclasses works:
class myfoo(bar_for_foo_mixin, foo):
def __init__(self):
print "myfoo __init__ called"
self.bar()
so in the mro() it would find a concrete version of bar() before it finds the abstract one. No idea if this is actually what happens in the background though.
Cheers, Lars
PS: the code that worked in python 2.7 (python 3 has a different way to set metaclasses) was:
class A(object):
__metaclass__ = abc.ABCMeta
#abc.abstractmethod
def do(self):
pass
class B(object):
def do(self):
print "do"
class C(B, A):
pass
c = C()

Python class method also instance method

I have a class that in principle carries all the information about it in its class body. When instantiated, it receives additional information that together with the class attributes forms a regular instance. My problem now lies in the fact that I need to implement a method which should be called as class method when it is called from a class object but should be called as regular instance method when called from an instance:
e.g. something like
class MyClass(object):
attribs = 1, 2, 3
def myMethod(self, args):
if isclass(self):
"do class stuff"
else:
"do instance stuff"
MyClass.myMethod(2) #should now be called as a class method, e.g. I would normally do #classmethod
MyClass().myMethod(2) #should now be called as instance method
Of course I could declare it as staticmethod and pass either the instance or the class object explicitly, but that seems rather unpythonic and also user unfriendly.
If the methods are to behave differently, you could simply change which one is exposed by that name at initialization time:
class MyCrazyClass:
#classmethod
def magicmeth(cls):
print("I'm a class")
def _magicmeth(self):
print("I'm an instance")
def __init__(self):
self.magicmeth = self._magicmeth
You can define a decorator that works like a regular method when called on an instance, or class method when called on a class. This requires a descriptor:
from functools import partial
class anymethod:
"""Transform a method into both a regular and class method"""
def __init__(self, call):
self.__wrapped__ = call
def __get__(self, instance, owner):
if instance is None: # called on class
return partial(self.__wrapped__, owner)
else: # called on instance
return partial(self.__wrapped__, instance)
class Foo:
#anymethod
def bar(first):
print(first)
Foo.bar() # <class '__main__.Foo'>
Foo().bar() # <__main__.Foo object at 0x106f86610>
Note that this behaviour will not be obvious to most programmers. Only use it if you really need it.

when not overriding an abstract method in a child class I am not getting an exception thrown

When running the following code I am expecting to get an exception thrown because ChildPizza is inheriting from BasePizza but is not implementing the abstract method get_radius. But the program runs with no errors. Why?
At the very least the last line should throw an exception.
from abc import ABCMeta, abstractmethod
class BasePizza(object):
__metaclass__ = ABCMeta
#abstractmethod
def get_radius(self):
raise NotImplementedError( "implement me in child class" )
class ChildPizza(BasePizza):
pass
myChildPizza = ChildPizza()
myBasePizza = BasePizza()
Python 3 doesn't use the __metaclass__ class attribute, so you aren't actually defining an abstract base class. Instead, you specify a keyword "argument" to the class definition.
class BasePizza(metaclass=ABCMeta):
#abstractmethod
def get_radius(self):
pass
You can also simply subclass abc.ABC, which is predefine to use ABCMeta as its metaclass.
class BasePizza(ABC):
#abstractmethod
def get_radius(self):
pass

Can python metaclasses inherit?

classes can inherit..
class Base:
def __init__(self,name):
self.name = name
class Derived1(Base):
def __init__(self,name):
super().__init__(name)
class Derived2(Base):
def __init__(self,name):
super().__init__(name)
Can a similar thing done for meta classes also?
I have a requirement where some of my classes will have to be both abstract base classes and also my own meta classes (say singleton types..)
Is it possible to do
class Singleton(type):
'''
implementation goes here..
'''
class AbstractSingleton(Singleton,ABCMeta):
'''
What code should go here??
'''
If its possible how to implement the AbstractSingleton class?
Yes, it is possible.
But first things first:
You should not be using metaclasses for creating singletons in Python.
Singletons are a simple concept, and just a custom __new__ method is enough - no need for a metaclass for that.
This simple 4 line normal class code can be used as a mixin, and will turn any derived classes into "singleton" classes - afer the first instance is created, no further instances are created, and the first instance is always returned:
class SingletonBase:
def __new__(cls, *args, **kw):
if not "instance" in cls.__dict__:
cls.instance = super().__new__(cls, *args, **kw)
return cls.instance
Now, if you'd have a real case for another metaclass and needed to combine that with ABCMeta or other metaclass, all you'd have to do is to create a third metaclass that inherits from both metaclasses - if both of them use super in a well behaved way, it would just work.
class SingletonMeta(type):
def __call__(cls, *args, **kw):
# You know - you _really_ should not be using metaclasses for singletons.
if not "instance" in cls.__dict__:
cls.instance = super().__call__(*args, **kw)
return cls.instance
class SingletonAbstractMeta(SingletonMeta, abc.ABCMeta):
pass
class SingleAbstractBase(metaclass=SingleAbstractMeta):
...
For sheer coincidence, earlier this week I used exactly this use case as an example of what can be achieved with a "meta meta class" in Python. By having a special "meta meta class" to the metaclass one wants to combine to another (I even use ABCMeta on the example), it can create the derived combined metaclass just by using the operator " + ", like in
class SingletonMeta(type, metaclass=MM):
...
class AbstractSingletonBase(metaclass=SingletonMeta + abc.ABCMeta):
# code here.
Check the answer here.

Python 3 metaclass defined functions not visible in the class derived from metaclass

I am working on a python 3 based metaclass, as i understand metaclass a typedef
of a class. Here is a small example
class smallClass(type):
def __init__(self, clsname, bases, clsdict):
super(smallClass, self).__init__(clsname, bases, clsdict)
self.a = 10
self.b = 15
print(self.addnos())
def addnos(self):
return self.a + self.b
def __new__(self, clsname, bases, clsdict):
return super(smallClass, self).__new__(self, clsname, bases, clsdict)
class smallClass2(object, metaclass=smallClass):
la = smallClass.addnos
clstest = smallClass2()
clstest.addnos() ''' this does not work: AttributeError: 'smallClass2' object has no attribute 'addnos' '''
strange thing is function addnos is not visible from smallClass2 even it is based on metaclass smallClass that included addnos function
can someone help to understand how to access functions defined in metaclass? Or why clstest.addnos() does not work?
Function addnos can be accessed from metaclass but not from derived class, why?
Here another alternative try, have the same problem
class smallClass(type):
def __init__(self, clsname, bases, clsdict):
super(smallClass, self).__init__(clsname, bases, clsdict)
self.a = 10
self.b = 15
print(self.addnos())
def addnos(self):
return self.a + self.b
def __new__(self, clsname, bases, clsdict):
return super(smallClass, self).__new__(self, clsname, bases, clsdict)
class smallClass2(object, metaclass=smallClass):
pass
clstest = smallClass2()
clstest.addnos()
I can't understand what is your intention, and whether you really need a metaclass at all. But here is what is happening:
A metaclass in Python is a class-object class. I think it is easy to visualize that with an example on the (iPython) terminal :
In [1]: class A:
...: pass
...:
...: a = A()
...:
In [2]: type(a)
Out[2]: __main__.A
In [3]: type(A)
Out[3]: type
In this case, "A", returned by type(a) is the class of the object "a", and "type" returned by "type(A)" is the class of the object "A" - the trick is that in Python even classes are objects: they are all instances of "type" or of a subtype of "type". This role of "class of a class" is what is termed "metaclass".
When ou inherit from "type", instances of the class ou create will be classes themselves. And there is syntactic support of the language, in the form of the "named parameter" on the class statement, to select the metaclass for a class body.
All that said, this is how attribute retrieval works in Python (shortened version - I won't enter into "descriptors" or the setattr, getattr and getattribute mechanisms which add a lot of steps):
When you request an attribute from an object, Python first(*) looks into that object's __dict__ attribute, which must be mapping, if it contains the requested attribute. If not, it looks for the attribute on the object's class (on the class __dict__ as well). If the class of the object does not have the attribute, Python looks for it on the superclasses of the object - but it does not step into the class'class to look for the attribute. (It follows the metaclasses as they endup in the class __mro__ attribute).
So, if I do:
class MyMeta(type):
test = "value from metaclass"
other_test = "Other metaclass value"
class A:
test = "value from ancestor class"
class B(A, metaclass=MyMeta):
pass
b = B()
For the instance "b", Python can find "test" as defined in "A", but it will never automatically find the "test" as defined in "MyMeta", nor it will see "other_test". This is what is taking place in your code.
If one needs to see from an instance values as they are defined in the metaclass, all one have to do is to step through the class - so, if a method in B wants to get to "other_test" above, this will work:
class B(A, metaclass=MyMeta):
def method(self):
print(self.__class__.other_test)
(Not that test, however would still be shadowed by the test defined in the ancestor class in A)
All said, I find it very little probable that you actually need a metaclass for whatever you are trying there. It looks more like you want the behavior for simple class inheritance.
(*) Always having in mind this is a super-simplifed description of the attribute lookup algorithm. The first lookup for an attribute is actually in the instance's class itself, looking for a "descriptor" object.

Resources