Python Decorated Class does not allow method calls. Why? - python

As i mentioned in this previous post. Im trying to create a decorator which does the following:
The decorated class represents a document in a Documentbased DB like CouchDB or MongoDB. The Decorator accepts on argument which is a instance of a connector to such a database. The Model Class (in this example User) does automatically map undefined attributes to fields in the DB.
Now i get stuck a bit :-/ The mentioned things is all working. But now i cant call any methods from the Model Class. I'm getting the following error.
TypeError: unbound method myfunc() must be called with User instance
as first argument (got nothing instead)
class Connector(object):
def readvar(self, var):
data = {"emailAddress":"jack.bauer#ctu.org", "lastName":"Bauer"}
return data[var]
class DocumentDB(object):
def __init__(self,connector):
self.connector = connector
def __call__(self, *args, **kargs):
_c = self.connector
class TransparentAttribute:
def __getattr__(self, attrname):
try:
return _c.readvar(attrname)
except:
return getattr(args[0], attrname)
return TransparentAttribute
c = Connector()
#DocumentDB(c)
class User(object):
username = "JackBauer"
def doSomething(self):
print "bla bla"
def doSomethingElse(self):
pass
def myfunc(self):
print "afadsadsf adsf asdf asdf"
u = User()
u.myfunc() # Does not work!!!
print u.emailAddress
print u.lastName
print u.username

I've had a quick play, and it looks like subclassing the user works.
class DocumentDB(object):
def __init__(self,connector):
self.connector = connector
def __call__(self, user):
_c = self.connector
print self, user, _c # <__main__.DocumentDB object at 0x012DAD30> <class '__main__.User'> <__main__.Connector object at 0x012DAD70>
class TransparentAttribute(user):
def __getattr__(self, attrname):
try:
return _c.readvar(attrname)
except:
return getattr(user, attrname)
return TransparentAttribute
u = User()
print type(u) # <class '__main__.TransparentAttribute'>
u.myfunc() # afadsadsf adsf asdf asdf
After u = User(), u is of type TransparentAttribute, and I think if you don't subclass it then you basically replace your User instance with a TransparentAttribute instance (so all the User object's local functions are gone).
(But to be honest, some of this is a bit over my head - feel free to correct me)

args[0] is the User class object, not an instance as you except, hence you get an unbound method (aka class method) and not a bound method.
#DocumentDB(c)
class User(object):
pass
can be rewritten as
class User(object):
pass
User = DocumentDB(c)(User)
which makes the problem more clear (btw, does TransparentAttribute deliberately not inherit from object?)
Maybe you can get what you want not with a decorator but by using Connector as an additional base class to User?

Related

Does function know about the class before binding

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.

Python: Storing class type on a class variable durig class initialization

I'm trying to initialize an objects field with a class that needs to know the type that is using it:
class Device(Model):
objects = AbstractManager(Device)
# the rest of the class here
This is how AbstractManager is defined:
class AbstractManager:
def __init__(self, cls: type):
self.cls = cls
def all(self):
result = []
for cls in self._get_subclasses():
result.extend(list(cls.objects.all()))
return result
def _get_subclasses(self):
return self.cls.__subclasses__()
So I can later call this and returns all() from all subclasses:
Device.objects.all()
The issue here is that I cannot use Device while initializing Device.objects, since Device is still not initialized.
As a work-around I'm initializing this outside of the class, but there's gotta be a better way:
class Device(Model):
objects = None
# the rest of the class here
Device.objects = AbstractManager(Device)
PD: I have a C#/C++ background, so maybe I'm thinking too much about this in a static-typing mindset, can't tell
You don't need to add any additional logic for this. Django allows you to access model class from manager using self.model attribute:
def _get_subclasses(self):
return self.model.__subclasses__()
You do not have to do that. Django will automatically call the contribute_to_class method, where it will pass the model, and for a manager, it will be stored in self.model. You can thus simply implement this as:
from django.db.models.manager import ManagerDescriptor
class AbstractManager(models.Manager):
def all(self):
result = []
for cls in self._get_subclasses():
result.extend(list(cls.objects.all()))
return result
def contribute_to_class(self, model, name):
self.name = self.name or name
self.model = model
setattr(model, name, AbstractManagerDescriptor(self))
model._meta.add_manager(self)
def _get_subclasses(self):
return self.model.__subclasses__()
class AbstractManagerDescriptor(ManagerDescriptor):
def __get__(self, instance, cls=None):
if instance is not None:
raise AttributeError("Manager isn't accessible via %s instances" % cls.__name__)
if cls._meta.swapped:
raise AttributeError(
"Manager isn't available; '%s.%s' has been swapped for '%s'" % (
cls._meta.app_label,
cls._meta.object_name,
cls._meta.swapped,
)
)
return cls._meta.managers_map[self.manager.name]
and add the manager as:
class Device(models.Model):
objects = AbstractManager()
That being said, I'm not sure that this is a good idea for two reasons:
you are returning a list, and normally .all() returns a QuerySet, you thus here "destroy" the laziness of the queryset, which can result in expensive querying; and
if one would use Device.objects.filter() for example, it would simply circumvent.
You might want to subclass the queryset, and then aim to implement that differently.

Class Design - Return subclass from base class initalization

I have two classes, with similar methods (read, write, insert) but because of the file types the produce, their methods must be implemented differently. Ideally, I would like the user to initialize a base type and the appropriate subtype is returned based on keywords during instantiation:
c = SomeThing() # returns subclass of type 1 (set in default)
c = Something(flag=True) # returns the other subclass
Initially I tried putting a return statement in the __init__ of the base class, but appparently __init__ should return None, so not sure where to set this; should I just create a base class factory method that returns the appropriate type?:
class SomeThing:
def __init__(self, flag=False):
self.build(flag)
def build(self, flag):
if not flag:
return SubclassOne()
reutnr SubclassTwo()
Or is there a better way for dynamically binding the appropriate methods based on keywords passed at instantiation? I wouldnt think this would be ideal:
class SomeThing:
def __init__(self, flag=False):
if not flag:
setattr(self, 'write', self.write_one)
else:
setattr(self, 'write', self.write_two)
def write_one(self):
# stuff
def write_two(self):
# stuff
Because the user of the interface could simply access the other methods, and I wouldnt want to define each method outside the classes because then the user could say do from something import write_one which would be inappropriate behavior.
I'd recommend you go with a factory of sorts:
class Base(object):
# ...
class SomeThing(Base):
# ...
class OtherThing(Base):
# ...
def create_thing(flag = False):
if flag:
return SomeThing()
else:
return OtherThing()

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.

Python decorator to limit a method to a particular class?

I've got a large library of Django apps that are shared by a handful of Django projects/sites. Within each project/site there is an option to define a 'Mix In' class that will be mixed in to one of the in-library base classes (which many models sub-class from).
For this example let's say the in-library base class is PermalinkBase and the mix-in class is ProjectPermalinkBaseMixIn.
Because so many models subclass from PermalinkBase, not all the methods/properities defined in ProjectPermalinkBaseMixIn will be utilitized by all of PermalinkBase's subclasses.
I'd like to write a decorator that can be applied to methods/properties within ProjectPermalinkBaseMixIn in order to limit them from running (or at least returning None) if they are accessed from a non-approved class.
Here's how I'm doing it now:
class ProjectPermalinkBaseMixIn(object):
"""
Project-specific Mix-In Class to `apps.base.models.PermalinkBase`
"""
def is_video_in_season(self, season):
# Ensure this only runs if it is being called from the video model
if self.__class__.__name__ != 'Video':
to_return = None
else:
videos_in_season = season.videos_in_this_season.all()
if self in list(videos_in_season):
to_return = True
else:
to_return False
return to_return
Here's how I'd like to do it:
class ProjectPermalinkBaseMixIn(object):
"""
Project-specific Mix-In Class to `apps.base.models.PermalinkBase`
"""
#limit_to_model('Video')
def is_video_in_season(self, season):
videos_in_season = season.videos_in_this_season.all()
if self in list(videos_in_season):
to_return = True
else:
to_return = False
return to_return
Is this possible with decorators? This answer helped me to better understand decorators but I couldn't figure out how to modify it to solve the problem I listed above.
Are decorators the right tool for this job? If so, how would I write the limit_to_model decorator function? If not, what would be the best way to approach this problem?
was looking at your problem and I think this might be an overcomplicated way to achieve what you are trying to do. However I wrote this bit of code:
def disallow_class(*klass_names):
def function_handler(fn):
def decorated(self, *args, **kwargs):
if self.__class__.__name__ in klass_names:
print "access denied to class: %s" % self.__class__.__name__
return None
return fn(self, *args, **kwargs)
return decorated
return function_handler
class MainClass(object):
#disallow_class('DisallowedClass', 'AnotherDisallowedClass')
def my_method(self, *args, **kwargs):
print "my_method running!! %s" % self
class DisallowedClass(MainClass): pass
class AnotherDisallowedClass(MainClass): pass
class AllowedClass(MainClass): pass
if __name__ == "__main__":
x = DisallowedClass()
y = AnotherDisallowedClass()
z = AllowedClass()
x.my_method()
y.my_method()
z.my_method()
If you run this bit of code on your command line the output will be something like:
access denied to class: DisallowedClass
access denied to class: AnotherDisallowedClass
my_method running!! <__main__.AllowedClass object at 0x7f2b7105ad50>
Regards

Categories