Does function know about the class before binding - python

Is there a way to access a class (where function is defined as a method) before there is an instance of that class?
class MyClass:
def method(self):
print("Calling me")
m1 = MyClass.method
instance = MyClass()
m2 = instance.method
print(m2.__self__.__class__) # <class 'MyClass'>
# how to access `MyClass` from `m1`?
For example I have m1 variable somewhere in my code and want to have a reference to MyClass the same way I can access it from bound method m2.__self__.__class__.
print(m1.__qualname__) # 'MyClass.method'
The only option I was able to find is __qualname__ which is a string containing name of the class.

The attribute __self__ itself is annotated by Python when the function is bound to an instance and become a method. (The code to that is run somewhere when running the __get__ code in the function, but passing an instance different than None).
So, as people pointed out, you have the option of getting the classname as a string by going through __qualname__. Otherwise, if the functions/methods for which you will need this feature are known beforehand, it is possible to create a decorator that will annotate their class when they are retrieved as a class attribute (in contrast to the native annotation which only takes place when retrieving then as an instance attribute):
class unboundmethod:
def __init__(self, func, cls):
self.__func__ = func
self.class_ = cls
self.__self__ = None
def __call__(self, instance, *args, **kw):
if not isinstance(instance, self.class_):
# This check is actually optional fancy stuff, since we are here! :-)
raise TypeError(f"First parameter fo {self.__func__.__name__} must be an instance of {self.class_}")
return self.__func__(instance, *args, **kw)
def __repr__(self):
return f"Unbound method {self.__func__!r} related to {self.class_}"
class clsbind:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
# the function is being retrieved from the class:
return unboundmethod(self.func, owner)
# return control to usual method creation codepath:
return self.func.__get__(instance, owner)
class MyClass:
#clsbind
def method(self):
print("Calling me")
And on the REPL you can have this:
In [136]: m1 = MyClass.method
In [137]: m1.class_
Out[137]: __main__.MyClass
In [138]: m1(MyClass())
Calling me

You can get the class instance using the __qualname__
my_class = eval(m1.__qualname__.split('.')[-2])
print(my_class)
Not the most generic and safest approach, but should work for this simple scenario.

Related

Using metaclasses in order to define methods, class methods / instance methods

I am trying to understand deeper how metaclasses work in python. My problem is the following, I want to use metaclasses in order to define a method for each class which would use a class attribute defined within the metaclass. For instance, this has application for registration.
Here is a working example:
import functools
def dec_register(func):
#functools.wraps(func)
def wrapper_register(*args, **kwargs):
(args[0].__class__.list_register_instances).append(args[0])
return func(*args, **kwargs)
return wrapper_register
dict_register_classes = {}
class register(type):
def __new__(meta, name, bases, attrs):
dict_register_classes[name] = cls = type.__new__(meta, name, bases, attrs) # assigniation from right to left
cls.list_register_instances = []
cls.print_register = meta.print_register
return cls
def print_register(self):
for element in self.list_register_instances:
print(element)
def print_register_class(cls):
for element in cls.list_register_instances:
print(element)
#
class Foo(metaclass=register):
#dec_register
def __init__(self):
pass
def print_register(self):
pass
class Boo(metaclass=register):
#dec_register
def __init__(self):
pass
def print_register(self):
pass
f = Foo()
f_ = Foo()
b = Boo()
print(f.list_register_instances)
print(b.list_register_instances)
print(dict_register_classes)
print("1")
f.print_register()
print("2")
Foo.print_register_class()
print("3")
f.print_register_class()
print("4")
Foo.print_register()
The test I am making at the end do not work as I was expected. I apologize in advance if what I am saying is not using the proper syntax, I am trying to be as clear as possible :
I was thinking that the line cls.print_register = meta.print_register is defining a method within the class using the method defined within the metaclass. Thus it is a method that I can use on an object. I can also use it a class method since it is defined in the metaclass. However, though the following works :
print("1")
f.print_register()
this do not work correctly :
print("4")
Foo.print_register()
with error :
Foo.print_register()
TypeError: print_register() missing 1 required positional argument: 'self'
Same for test 2 and 3, where I was expecting that if a method is defined on the class level, it should also be defined on the object level. However, test 3 is raising an error.
print("2")
Foo.print_register_class()
print("3")
f.print_register_class()
Hence, can you please explain me how come my understanding of class methods is wrong ? I would like to be able to call the method print_register either on the class or on the object.
Perhaps it could help to know that in fact I was trying to reproduce the following very simple example :
# example without anything fancy:
class Foo:
list_register_instances = []
def __init__(self):
self.__class__.list_register_instances.append(self)
#classmethod
def print_register(cls):
for element in cls.list_register_instances:
print(element)
Am I not doing the exact same thing with a metaclass ? A classmethod can be used either on a class or on objects.
Also if you have any tips about code structure I would greatly appreciate it. I must be very bad at the syntax of metaclasses.
Fundamentally, because you have shadowed print_register on your instance of the metaclass (your class).
So when you do Foo.print_register, it finds the print_register you defined in
class Foo(metaclass=register):
...
def print_register(self):
pass
Which of course, is just the plain function print_register, which requires the self argument.
This is (almost) the same thing that would happen with just a regular class and it's instances:
class Foo:
def bar(self):
print("I am a bar")
foo = Foo()
foo.bar = lambda x: print("I've hijacked bar")
foo.bar()
Note:
In [1]: class Meta(type):
...: def print_register(self):
...: print('hi')
...:
In [2]: class Foo(metaclass=Meta):
...: pass
...:
In [3]: Foo.print_register()
hi
In [4]: class Foo(metaclass=Meta):
...: def print_register(self):
...: print('hello')
...:
In [5]: Foo.print_register()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-a42427fde947> in <module>
----> 1 Foo.print_register()
TypeError: print_register() missing 1 required positional argument: 'self'
However, you do this in your metaclass constructor as well!
cls.print_register = meta.print_register
Which is effectively like defining that function in your class definition... not sure why you are doing this though.
You are not doing the exact same thing as using a classmethod, which is a custom descriptor that handles the binding of methods to instances in just the way you'd need to be able to call it on a class or on an instance. That is not the same as defining a method on the class and on the instance! You could just do this in your metaclass __new__, i.e. cls.print_register = classmethod(meta.print_register) and leave def print_register(self) out of your class definitions:
import functools
def dec_register(func):
#functools.wraps(func)
def wrapper_register(*args, **kwargs):
(args[0].__class__.list_register_instances).append(args[0])
return func(*args, **kwargs)
return wrapper_register
dict_register_classes = {}
class register(type):
def __new__(meta, name, bases, attrs):
dict_register_classes[name] = cls = type.__new__(meta, name, bases, attrs) # assigniation from right to left
cls.list_register_instances = []
cls.print_register = classmethod(meta.print_register) # just create the classmethod manually!
return cls
def print_register(self):
for element in self.list_register_instances:
print(element)
def print_register_class(cls):
for element in cls.list_register_instances:
print(element)
#
class Foo(metaclass=register):
#dec_register
def __init__(self):
pass
Note, print_register doesn't have to be defined inside your metaclass, indeed, in this case, I would just define it at the module level:
def print_register(self):
for element in self.list_register_instances:
print(element)
...
class register(type):
def __new__(meta, name, bases, attrs):
dict_register_classes[name] = cls = type.__new__(meta, name, bases, attrs) # assigniation from right to left
cls.list_register_instances = []
cls.print_register = classmethod(print_register)
return cls
...
I think you understand metaclasses sufficiently, actually, it is your understanding of classmethod that is incorrect, as far as I can tell. If you want to understand how classmethod works, indeed, how method-instance binding works for regular functions, you need to understand descriptors. Here's an enlightening link. Function objects are descriptors, they bind the instance as the first argument to themselves when called on an instance (rather, they create a method object and return that, but it is basically partial application). classmethod objects are another kind of descriptor, one that binds the class to the first argument to the function it decorates when called on either the class or the instance. The link describes how you could write classmethod using pure python.

Class with a registry of methods based on decorators

I have a class that has several methods which each have certain properties (in the sense of quality). I'd like these methods to be available in a list inside the class so they can be executed at once. Note that the properties can be interchangeable so this can't be solved by using further classes that would inherit from the original one. In an ideal world it would look something like this:
class MyClass:
def __init__():
red_rules = set()
blue_rules = set()
hard_rules = set()
soft_rules = set()
#red
def rule_one(self):
return 1
#blue
#hard
def rule_two(self):
return 2
#hard
def rule_three(self):
return 3
#blue
#soft
def rule_four(self):
return 4
When the class is instantiated, it should be easy to simply execute all red and soft rules by combining the sets and executing the methods. The decorators for this are tricky though since a regular registering decorator can fill out a global object but not the class attribute:
def red(fn):
red_rules.add(fn)
return fn
How do I go about implementing something like this?
You can subclass set and give it a decorator method:
class MySet(set):
def register(self, method):
self.add(method)
return method
class MyClass:
red_rules = MySet()
blue_rules = MySet()
hard_rules = MySet()
soft_rules = MySet()
#red_rules.register
def rule_one(self):
return 1
#blue_rules.register
#hard_rules.register
def rule_two(self):
return 2
#hard_rules.register
def rule_three(self):
return 3
#blue_rules.register
#soft_rules.register
def rule_four(self):
return 4
Or if you find using the .register method ugly, you can always define the __call__ method to use the set itself as a decorator:
class MySet(set):
def __call__(self, method):
"""Use set as a decorator to add elements to it."""
self.add(method)
return method
class MyClass:
red_rules = MySet()
...
#red_rules
def rule_one(self):
return 1
...
This looks better, but it's less explicit, so for other collaborators (or future yourself) it might be harder to grasp what's happening here.
To call the stored functions, you can just loop over the set you want and pass in the instance as the self argument:
my_instance = MyClass()
for rule in MyClass.red_rules:
rule(my_instance)
You can also create an utility function to do this for you, for example you can create a MySet.invoke() method:
class MySet(set):
...
def invoke(self, obj):
for rule in self:
rule(obj)
And now just call:
MyClass.red_rules.invoke(my_instance)
Or you could have MyClass handle this instead:
class MyClass:
...
def invoke_rules(self, rules):
for rule in rules:
rule(self)
And then call this on an instance of MyClass:
my_instance.invoke_rules(MyClass.red_rules)
Decorators are applied when the function is defined; in a class that's when the class is defined. At this point in time there are no instances yet!
You have three options:
Register your decorators at the class level. This is not as clean as it may sound; you either have to explicitly pass additional objects to your decorators (red_rules = set(), then #red(red_rules) so the decorator factory can then add the function to the right location), or you have to use some kind of class initialiser to pick up specially marked functions; you could do this with a base class that defines the __init_subclass__ class method, at which point you can iterate over the namespace and find those markers (attributes set by the decorators).
Have your __init__ method (or a __new__ method) loop over all the methods on the class and look for special attributes the decorators have put there.
The decorator would only need to add a _rule_name or similar attribute to decorated methods, and {getattr(self, name) for for name in dir(self) if getattr(getattr(self, name), '_rule_name', None) == rule_name} would pick up any method that has the right rule name defined in rule_name.
Make your decorators produce new descriptor objects; descriptors have their __set_name__() method called when the class object is created. This gives you access to the class, and thus you can add attributes to that class.
Note that __init_subclass__ and __set_name__ require Python 3.6 or newer; you'd have to resort to a metaclass to achieve similar functionality in earlier versions.
Also note that when you register functions at the class level, that you need to then explicitly bind them with function.__get__(self, type(cls)) to turn them into methods, or you can explicitly pass in self when calling them. You could automate this by making a dedicated class to hold the rule sets, and make this class a descriptor too:
import types
from collections.abc import MutableSet
class RulesSet(MutableSet):
def __init__(self, values=(), rules=None, instance=None, owner=None):
self._rules = rules or set() # can be a shared set!
self._instance = instance
self._owner = owner
self |= values
def __repr__(self):
bound = ''
if self._owner is not None:
bound = f', instance={self._instance!r}, owner={self._owner!r}'
rules = ', '.join([repr(v) for v in iter(self)])
return f'{type(self).__name__}({{{rules}}}{bound})'
def __contains__(self, ob):
try:
if ob.__self__ is self._instance or ob.__self__ is self._owner:
# test for the unbound function instead when both are bound, this requires staticmethod and classmethod to be unwrapped!
ob = ob.__func__
return any(ob is getattr(f, '__func__', f) for f in self._rules)
except AttributeError:
# not a method-like object
pass
return ob in self._rules
def __iter__(self):
if self._owner is not None:
return (f.__get__(self._instance, self._owner) for f in self._rules)
return iter(self._rules)
def __len__(self):
return len(self._rules)
def add(self, ob):
while isinstance(ob, Rule):
# remove any rule wrappers
ob = ob._function
assert isinstance(ob, (types.FunctionType, classmethod, staticmethod))
self._rules.add(ob)
def discard(self, ob):
self._rules.discard(ob)
def __get__(self, instance, owner):
# share the set with a new, bound instance.
return type(self)(rules=self._rules, instance=instance, owner=owner)
class Rule:
#classmethod
def make_decorator(cls, rulename):
ruleset_name = f'{rulename}_rules'
def decorator(f):
return cls(f, ruleset_name)
decorator.__name__ = rulename
return decorator
def __init__(self, function, ruleset_name):
self._function = function
self._ruleset_name = ruleset_name
def __get__(self, *args):
# this is mostly here just to make Python call __set_name__
return self._function.__get__(*args)
def __set_name__(self, owner, name):
# register, then replace the name with the original function
# to avoid being a performance bottleneck
ruleset = getattr(owner, self._ruleset_name, None)
if ruleset is None:
ruleset = RulesSet()
setattr(owner, self._ruleset_name, ruleset)
ruleset.add(self)
# transfer controrol to any further rule objects
if isinstance(self._function, Rule):
self._function.__set_name__(owner, name)
else:
setattr(owner, name, self._function)
red = Rule.make_decorator('red')
blue = Rule.make_decorator('blue')
hard = Rule.make_decorator('hard')
soft = Rule.make_decorator('soft')
Then just use:
class MyClass:
#red
def rule_one(self):
return 1
#blue
#hard
def rule_two(self):
return 2
#hard
def rule_three(self):
return 3
#blue
#soft
def rule_four(self):
return 4
and you can access self.red_rules, etc. as a set with bound methods:
>>> inst = MyClass()
>>> inst.red_rules
RulesSet({<bound method MyClass.rule_one of <__main__.MyClass object at 0x106fe7550>>}, instance=<__main__.MyClass object at 0x106fe7550>, owner=<class '__main__.MyClass'>)
>>> inst.blue_rules
RulesSet({<bound method MyClass.rule_two of <__main__.MyClass object at 0x106fe7550>>, <bound method MyClass.rule_four of <__main__.MyClass object at 0x106fe7550>>}, instance=<__main__.MyClass object at 0x106fe7550>, owner=<class '__main__.MyClass'>)
>>> inst.hard_rules
RulesSet({<bound method MyClass.rule_three of <__main__.MyClass object at 0x106fe7550>>, <bound method MyClass.rule_two of <__main__.MyClass object at 0x106fe7550>>}, instance=<__main__.MyClass object at 0x106fe7550>, owner=<class '__main__.MyClass'>)
>>> inst.soft_rules
RulesSet({<bound method MyClass.rule_four of <__main__.MyClass object at 0x106fe7550>>}, instance=<__main__.MyClass object at 0x106fe7550>, owner=<class '__main__.MyClass'>)
>>> for rule in inst.hard_rules:
... rule()
...
2
3
The same rules are accessible on the class; normal functions remain unbound:
>>> MyClass.blue_rules
RulesSet({<function MyClass.rule_two at 0x107077a60>, <function MyClass.rule_four at 0x107077b70>}, instance=None, owner=<class '__main__.MyClass'>)
>>> next(iter(MyClass.blue_rules))
<function MyClass.rule_two at 0x107077a60>
Containment testing works as expected:
>>> inst.rule_two in inst.hard_rules
True
>>> inst.rule_two in inst.soft_rules
False
>>> MyClass.rule_two in MyClass.hard_rules
True
>>> MyClass.rule_two in inst.hard_rules
True
You can use these rules to register classmethod and staticmethod objects too:
>>> class Foo:
... #hard
... #classmethod
... def rule_class(cls):
... return f'rule_class of {cls!r}'
...
>>> Foo.hard_rules
RulesSet({<bound method Foo.rule_class of <class '__main__.Foo'>>}, instance=None, owner=<class '__main__.Foo'>)
>>> next(iter(Foo.hard_rules))()
"rule_class of <class '__main__.Foo'>"
>>> Foo.rule_class in Foo.hard_rules
True

Accessing self in a function attribute

I'm trying to add a decorator that adds callable attributes to functions that return slightly different objects than the return value of the function, but will execute the function at some point.
The problem I'm running into is that when the function object is passed into the decorator, it is unbound and doesn't contain the implicit self argument. When I call the created attribute function (ie. string()), I don't have access to self and can't pass it into the original function.
def deco(func):
"""
Add an attribute to the function takes the same arguments as the
function but modifies the output.
"""
def string(*args, **kwargs):
return str(func(*args, **kwargs))
func.string = string
return func
class Test(object):
def __init__(self, value):
self._value = 1
#deco
def plus(self, n):
return self._value + n
When I go to execute the attribute created by the decorator, this is the error I get, because args doesn't contain the self reference.
>>> t = Test(100)
>>> t.plus(1) # Gets passed self implicitly
101
>>> t.plus.string(1) # Does not get passed self implicitly
...
TypeError: plus() takes exactly 2 arguments (1 given)
Is there a way to create a decorator like this that can get a reference to self? Or is there a way to bind the added attribute function (string()) so that it also gets called with the implicit self argument?
You can use descriptors here:
class deco(object):
def __init__(self, func):
self.func = func
self.parent_obj = None
def __get__(self, obj, type=None):
self.parent_obj = obj
return self
def __call__(self, *args, **kwargs):
return self.func(self.parent_obj, *args, **kwargs)
def string(self, *args, **kwargs):
return str(self(*args, **kwargs))
class Test(object):
def __init__(self, value):
self._value = value
#deco
def plus(self, n):
return self._value + n
so that:
>>> test = Test(3)
>>> test.plus(1)
4
>>> test.plus.string(1)
'4'
This warrants an explanation. deco is a decorator, but it is also a descriptor. A descriptor is an object that defines alternative behavior that is to be invoked when the object is looked up as an attribute of its parent. Interestingly, bounds methods are themselves implemented using the descriptor protocol
That's a mouthful. Let's look at what happens when we run the example code. First, when we define the plus method, we apply the deco decorator. Now normally we see functions as decorators, and the return value of the function is the decorated result. Here we are using a class as a decorator. As a result, Test.plus isn't a function, but rather an instance of the deco type. This instance contains a reference to the plus function that we wish to wrap.
The deco class has a __call__ method that allows instances of it to act like functions. This implementation simply passes the arguments given to the plus function it has a reference to. Note that the first argument will be the reference to the Test instance.
The tricky part comes in implementing test.plus.string(1). To do this, we need a reference to the test instance of which the plus instance is an attribute. To accomplish this, we use the descriptor protocol. That is, we define a __get__ method which will be invoked whenever the deco instance is accessed as an attribute of some parent class instance. When this happens, it stores the parent object inside itself. Then we can simply implement plus.string as a method on the deco class, and use the reference to the parent object stored within the deco instance to get at the test instance to which plus belongs.
This is a lot of magic, so here's a disclaimer: Though this looks cool, it's probably not a great idea to implement something like this.
You need to decorate your function at instantiation time (before creating the instance method). You can do this by overriding the __new__ method:
class Test(object):
def __new__(cls, *args_, **kwargs_):
def deco(func):
def string(*args, **kwargs):
return "my_str is :" + str(func(*args, **kwargs))
# *1
func.__func__.string = string
return func
obj = object.__new__(cls, *args_, **kwargs_)
setattr(obj, 'plus', deco(getattr(obj, 'plus')))
return obj
def __init__(self, value):
self._value = 1
def plus(self, n):
return self._value + n
Demo:
>>> t = Test(100)
>>> t.plus(1)
>>> t.plus.string(5)
>>> 'my_str is :6'
1. Since python doesn't let you access the real instance attribute at setting time you can use __func__ method in order to access the real function object of the instance method.

Same name for classmethod and instancemethod

I'd like to do something like this:
class X:
#classmethod
def id(cls):
return cls.__name__
def id(self):
return self.__class__.__name__
And now call id() for either the class or an instance of it:
>>> X.id()
'X'
>>> X().id()
'X'
Obviously, this exact code doesn't work, but is there a similar way to make it work?
Or any other workarounds to get such behavior without too much "hacky" stuff?
Class and instance methods live in the same namespace and you cannot reuse names like that; the last definition of id will win in that case.
The class method will continue to work on instances however, there is no need to create a separate instance method; just use:
class X:
#classmethod
def id(cls):
return cls.__name__
because the method continues to be bound to the class:
>>> class X:
... #classmethod
... def id(cls):
... return cls.__name__
...
>>> X.id()
'X'
>>> X().id()
'X'
This is explicitly documented:
It can be called either on the class (such as C.f()) or on an instance (such as C().f()). The instance is ignored except for its class.
If you do need distinguish between binding to the class and an instance
If you need a method to work differently based on where it is being used on; bound to a class when accessed on the class, bound to the instance when accessed on the instance, you'll need to create a custom descriptor object.
The descriptor API is how Python causes functions to be bound as methods, and bind classmethod objects to the class; see the descriptor howto.
You can provide your own descriptor for methods by creating an object that has a __get__ method. Here is a simple one that switches what the method is bound to based on context, if the first argument to __get__ is None, then the descriptor is being bound to a class, otherwise it is being bound to an instance:
class class_or_instancemethod(classmethod):
def __get__(self, instance, type_):
descr_get = super().__get__ if instance is None else self.__func__.__get__
return descr_get(instance, type_)
This re-uses classmethod and only re-defines how it handles binding, delegating the original implementation for instance is None, and to the standard function __get__ implementation otherwise.
Note that in the method itself, you may then have to test, what it is bound to. isinstance(firstargument, type) is a good test for this:
>>> class X:
... #class_or_instancemethod
... def foo(self_or_cls):
... if isinstance(self_or_cls, type):
... return f"bound to the class, {self_or_cls}"
... else:
... return f"bound to the instance, {self_or_cls"
...
>>> X.foo()
"bound to the class, <class '__main__.X'>"
>>> X().foo()
'bound to the instance, <__main__.X object at 0x10ac7d580>'
An alternative implementation could use two functions, one for when bound to a class, the other when bound to an instance:
class hybridmethod:
def __init__(self, fclass, finstance=None, doc=None):
self.fclass = fclass
self.finstance = finstance
self.__doc__ = doc or fclass.__doc__
# support use on abstract base classes
self.__isabstractmethod__ = bool(
getattr(fclass, '__isabstractmethod__', False)
)
def classmethod(self, fclass):
return type(self)(fclass, self.finstance, None)
def instancemethod(self, finstance):
return type(self)(self.fclass, finstance, self.__doc__)
def __get__(self, instance, cls):
if instance is None or self.finstance is None:
# either bound to the class, or no instance method available
return self.fclass.__get__(cls, None)
return self.finstance.__get__(instance, cls)
This then is a classmethod with an optional instance method. Use it like you'd use a property object; decorate the instance method with #<name>.instancemethod:
>>> class X:
... #hybridmethod
... def bar(cls):
... return f"bound to the class, {cls}"
... #bar.instancemethod
... def bar(self):
... return f"bound to the instance, {self}"
...
>>> X.bar()
"bound to the class, <class '__main__.X'>"
>>> X().bar()
'bound to the instance, <__main__.X object at 0x10a010f70>'
Personally, my advice is to be cautious about using this; the exact same method altering behaviour based on the context can be confusing to use. However, there are use-cases for this, such as SQLAlchemy's differentiation between SQL objects and SQL values, where column objects in a model switch behaviour like this; see their Hybrid Attributes documentation. The implementation for this follows the exact same pattern as my hybridmethod class above.
I have no idea what's your actual use case is, but you can do something like this using a descriptor:
class Desc(object):
def __get__(self, ins, typ):
if ins is None:
print 'Called by a class.'
return lambda : typ.__name__
else:
print 'Called by an instance.'
return lambda : ins.__class__.__name__
class X(object):
id = Desc()
x = X()
print x.id()
print X.id()
Output
Called by an instance.
X
Called by a class.
X
It can be done, quite succinctly, by binding the instance-bound version of your method explicitly to the instance (rather than to the class). Python will invoke the instance attribute found in Class().__dict__ when Class().foo() is called (because it searches the instance's __dict__ before the class'), and the class-bound method found in Class.__dict__ when Class.foo() is called.
This has a number of potential use cases, though whether they are anti-patterns is open for debate:
class Test:
def __init__(self):
self.check = self.__check
#staticmethod
def check():
print('Called as class')
def __check(self):
print('Called as instance, probably')
>>> Test.check()
Called as class
>>> Test().check()
Called as instance, probably
Or... let's say we want to be able to abuse stuff like map():
class Str(str):
def __init__(self, *args):
self.split = self.__split
#staticmethod
def split(sep=None, maxsplit=-1):
return lambda string: string.split(sep, maxsplit)
def __split(self, sep=None, maxsplit=-1):
return super().split(sep, maxsplit)
>>> s = Str('w-o-w')
>>> s.split('-')
['w', 'o', 'w']
>>> Str.split('-')(s)
['w', 'o', 'w']
>>> list(map(Str.split('-'), [s]*3))
[['w', 'o', 'w'], ['w', 'o', 'w'], ['w', 'o', 'w']]
"types" provides something quite interesting since Python 3.4: DynamicClassAttribute
It is not doing 100% of what you had in mind, but it seems to be closely related, and you might need to tweak a bit my metaclass but, rougly, you can have this;
from types import DynamicClassAttribute
class XMeta(type):
def __getattr__(self, value):
if value == 'id':
return XMeta.id # You may want to change a bit that line.
#property
def id(self):
return "Class {}".format(self.__name__)
That would define your class attribute. For the instance attribute:
class X(metaclass=XMeta):
#DynamicClassAttribute
def id(self):
return "Instance {}".format(self.__class__.__name__)
It might be a bit overkill especially if you want to stay away from metaclasses. It's a trick I'd like to explore on my side, so I just wanted to share this hidden jewel, in case you can polish it and make it shine!
>>> X().id
'Instance X'
>>> X.id
'Class X'
Voila...
In your example, you could simply delete the second method entirely, since both the staticmethod and the class method do the same thing.
If you wanted them to do different things:
class X:
def id(self=None):
if self is None:
# It's being called as a static method
else:
# It's being called as an instance method
(Python 3 only) Elaborating on the idea of a pure-Python implementation of #classmethod, we can declare an #class_or_instance_method as a decorator, which is actually a class implementing the attribute descriptor protocol:
import inspect
class class_or_instance_method(object):
def __init__(self, f):
self.f = f
def __get__(self, instance, owner):
if instance is not None:
class_or_instance = instance
else:
class_or_instance = owner
def newfunc(*args, **kwargs):
return self.f(class_or_instance, *args, **kwargs)
return newfunc
class A:
#class_or_instance_method
def foo(self_or_cls, a, b, c=None):
if inspect.isclass(self_or_cls):
print("Called as a class method")
else:
print("Called as an instance method")

Is possible to create a non-existent method when it is called in Python?

If I have this class:
class MyClass(object):
pass
And then I do it:
instance = MyClass()
instance.new_method()
I got an AttributeError Exception, but I want to create this method dinamically and return an especifc value. Is it possible?
Firstly Python checks if attribute with such name exists, if yes, it will call it. There's no clear way to prematurely detect whether this attribute will be called or not.
Here's the tricky way to achieve what you want:
class Dispatcher(object):
def __init__(self, caller, name):
self.name = name
self.caller = caller
def __call__(self, *a, **ka):
print('Call on Dispatcher registered!',
'Will create method on',
self.caller.__class__.__name__,
'now.')
setattr(self.caller, self.name, self.mock)
return getattr(self.caller, self.name)(*a, **ka)
#classmethod
def mock(cls, *a, **ka):
return 'Some default value for newly created methods.'
class MyClass(object):
def __getattr__(self, attr):
return Dispatcher(self, attr)
instance = MyClass()
print(instance.new_method, '\n')
print(instance.new_method(), '\n')
print(instance.new_method(), '\n')
print(instance.other_method)
Output:
<__main__.Dispatcher object at 0x0000000002C07DD8>
Call on Dispatcher registered! Will create method on MyClass now.
Some default value for newly created methods.
Some default value for newly created methods.
<__main__.Dispatcher object at 0x0000000002C07DD8>
Although this solution is comprehensive, it will return the new instance of Dispatcher every time you try to access non-existent attribute.
If Dispatcher instance is called (e.g Dispatcher(self, attr)()), it will set mock as a new method named attr to the object, passed as the first argument to the constructor.
Yes, you can do it as:
class MyClass(object):
pass
def some_method():
pass
name = 'new_method'
setattr(MyClass, name, classmethod(some_method))
It is possible.
>>> class MyClass(object):
pass
>>> instance = MyClass()
>>> def new_method(cls, x):
print x
>>> MyClass.new_method = new_method
>>> instance.new_method(45)
45
Note that the new_method has cls as the first parameter which (the instance) is passed implicitly when called as an instance method.

Categories