Why is get_default_prefix #classmethod instead of just a regular method - python

In formsets.py, you find this code snippet
class BaseFormSet(StrAndUnicode):
"""
A collection of instances of the same Form class.
"""
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList):
...
self.prefix = prefix or self.get_default_prefix() # Note the self.get_default_prefix
...
...
#classmethod # Note the #classmethod
def get_default_prefix(cls):
return 'form'
Why is get_default_prefix declared this way and then called with self.? Is there something gained by doing it this way? get_default_prefix has another definition in BaseInlineFormSet (forms/models.py)
class BaseInlineFormSet(BaseModelFormSet):
...
#classmethod
def get_default_prefix(cls):
from django.db.models.fields.related import RelatedObject
return RelatedObject(cls.fk.rel.to, cls.model, cls.fk).get_accessor_name().replace('+','')
and another in BaseGenericInlineFormset again using the #classmethod, so it doesn't appear to be a typo. I just don't understand why it would be done this way and then called with self.
The only clue I see (which I don't understand) is that the admin seems to call it with FormSet.get_default_prefix()
I'm wondering if there is something I'm just not understanding about python.

Calling a class method from an instance is perfectly legal, as you can see in the code. A related stackoverflow post said there was no benefit, (and it is bad practice) to call from an instance; because if you are only calling from instance your method should probably not be a classmethod.
I think you answer your own question, though. If django is calling FormSet.get_default_prefix() from somewhere, then they probably didn't want to instantiate a formset object

Related

Why can't I use #staticmethod here?

I'm using django-fsm to implement a state machine. The code looks like
def user_ok_to_check_me( instance, user):
...
class Job( models.Model):
# ... many screenfulls of code
#transition( field=state, target=BOOKING, source=CHECKING, permission=user_ok_to_check_me)
def fail_checking(self, **kwargs):
...
and it's working. Code readability is impaired by having that little utility function outside the class it belongs with, so I tried
class Job( models.Model):
# ... many screenfulls of code
#staticmethod
def user_ok_to_check_me( instance, user):
...
#transition( field=state, target=BOOKING, source=CHECKING, permission=user_ok_to_check_me)
def fail_checking(self, **kwargs):
which does not work. Not sure what user_ok_to_check_me does now do, it behaves like a no-op function always returning True even when all it does is return False
Why? And is there any way to declare this little function inside the class? (It's just a little bit too long to use lambda instance, user: )
Have found the answer, although I'm not sure I understand it.
class Job( models.Model):
# ... many screenfulls of code
# #staticmethod #NO DECORATOR
def user_ok_to_check_me( instance, user):
...
#transition( field=state, target=BOOKING, source=CHECKING, permission=user_ok_to_check_me)
def fail_checking(self, **kwargs):
The use of ok_to_check_me in #transition occurs during the execution of the code that creates the class, and not during the instantiation thereof. So it needs a reference to the actual function defined above. Application of #staticmethod to that function replaces it by something else, and whatever that is, is not acceptable to the transition decorator.
When the class is instantiated, the function gets bound to the instance. This does not, however, affect the reference to the function which #transition has already stored in its internals. In this case the binding is harmless since instance and self normally refer to the same. In other cases one might want to delete the un-intended bound function from the instance in its __init__ method (or just heavily document not to try to use it as an object method).

How to pass class attributes into method decorators?

I am trying to make a class that will make api requests, retrying based on configuration options passed in to the retrying.retry decorator, and handle different error codes in the correct way for each job.
Here is my code:
from retrying import retry
class APIRequester:
def __init__(self, url, **kwargs):
self.url = url
self.retry_kwargs = kwargs
#retry(**self.retry_kwargs) # Obviously doesn't know what self is
def make_request(self):
pass
How can I pass in parameters to this method decorator? I tried making them a class attribute, which didn't work either.
A couple of notes/questions:
The #retry decorator will be applied to the make_request method at the time the class is created, while retry_kwargs will only become available when an instance of the class is created, and thus the former must precede the latter.
In which case, the former cannot depend on information that becomes available in the latter, ... as long as you use the decorator syntax ...
The decorator syntax
#decorator
def xxx(...):
...
is just syntax sugar for
def xxx(...):
...
xxx = decorate(xxx)
which means that, along with the fact that Python is very dynamic, you could force the issue by doing something like
class APIRequester:
def __init__(self, url, **kwargs):
self.url = url
self.retry_kwargs = kwargs
APIRequester.make_request = retry(**kwargs)(APIRequester.make_request)
def make_request(self):
pass
Whether this particular decorator chokes on the self parameter or not, I cannot tell you.
Will you have more than one instance of APIRequester? If so, note that the method will be re-decorated each time a new instance is created: can this work sensibly? (I doubt it.) But see the edit below ...
If you do not have more that one instance, then you probably don't need to rely on information that becomes availale at the singleton's construction time.
The above were some general Python principles. I doubt that you really want to force the issue in this case. It seems to me that you are trying to use the decorator in a way that it was not designed to be used.
Edit: instancemethods
If you replace the line that does the decorating in the constructor with
self.make_request = retry(**kwargs)(self.make_request)
then each instance will get its own decorated version of the function. This should avoid any problems with re-decoration of the same function. There may will still be problems with self getting in the way. In that case, you could remove the self parameter from the definition and wrap it with staticmethod:
self.make_request = retry(**kwargs)(staticmethod(self.make_request))
Or better still, use decorator syntax to apply staticmethod to make_request at the place where you define it, the way Guido inteded it.
Like this, it even stands a chance of working! :-)
Decorator is just a syntax sugar for func=decorator(func). You can do the assignment yourself:
class APIRequester:
def __init__(self, url, **kwargs):
self.url = url
self.make_request = retry(**kwargs)(self.make_request)
def make_request(self):
pass
This will internally replace a method (descriptor) by a function, but it will work as expected.
Of course self is available in the decorator at the call time. See answers to How to decorate a method inside a class? , on which I based my answer here:
def my_retry(fn):
from functools import wraps
#wraps(fn)
def wrapped(self):
print(self.retry_kwargs)
for i in range(self.retry_kwargs["times"]):
# you have total control
fn(self)
# around your method. Can even call it multiple times,
# call with original retry:
retry(**self.retry_kwargs)(fn)(self)
# check exceptions, return some value (here None), etc
#
return wrapped
class APIRequester(object):
def __init__(self, url, **kwargs):
self.url = url
self.retry_kwargs = kwargs
#my_retry
def make_request(self):
print("method")
a = APIRequester('http://something', times=3)
a.make_request()
That is, original decorator is wrapped with a new, configuration-aware decorator. No need to change the constructor, syntax remains simple.
Retry decorator doesn't support class method, because instance of the class is implicitly passed to function.
Please decorate normal function.
If you want to wrap function into class, please decorate static method.

Is calling __new__ from a classmethod a pythonic way of doing?

I want to build a class, that is handling and processing some data. So I want property to handle all the data processing silently when new data is passed And I want to overload init with classmethod, to have flexibility on parameters passed to instance creation. So I came up with the following solution :
class Cooper():
def __init__(self):
...create all 'private' attributes
#classmethod
def Dougie(cls,data,datatype):
inst = cls.__new__(cls)
setattr(inst,datatype,data)
return inst
#property
def datatype1(self):
return self._datatype1
#datatype1.setter
def datatype1(self,newdata):
self._datatype1,self._datatype2,... = updatedata1(newdata)
#property
def datatype2(self):
return self._datatype2
#datatype2.setter
def datatype2(self,newdata):
self._datatype1,self._datatype2,... = updatedata2(newdata)
... to be continued...
Is this a pythonic way ? Or shoud I really create a metaclass (I get a little fir afraid there) ? What are the caveats ? Is there a better way ?

Using a base class function that takes parameters as a decorator for derived class function

I feel like I have a pretty good grasp on using decorators when dealing with regular functions, but between using methods of base classes for decorators in derived classes, and passing parameters to said decorators, I cannot figure out what to do next.
Here is a snippet of code.
class ValidatedObject:
...
def apply_validation(self, field_name, code):
def wrap(self, f):
self._validations.append(Validation(field_name, code, f))
return f
return wrap
class test(ValidatedObject):
....
#apply_validation("_name", "oh no!")
def name_validation(self, name):
return name == "jacob"
If I try this as is, I get an "apply_validation" is not found.
If I try it with #self.apply_validation I get a "self" isn't found.
I've also been messing around with making apply_validation a class method without success.
Would someone please explain what I'm doing wrong, and the best way to fix this? Thank you.
The issue you're having is that apply_validation is a method, which means you need to call it on an instance of ValidatedObject. Unfortunately, at the time it is being called (during the definition of the test class), there is no appropriate instance available. You need a different approach.
The most obvious one is to use a metaclass that searches through its instance dictionaries (which are really class dictionaries) and sets up the _validations variable based on what it finds. You can still use a decorator, but it probably should be a global function, or perhaps a static method, and it will need to work differently. Here's some code, that uses a metaclass and a decorator that adds function attributes:
class ValidatedMeta(type):
def __new__(meta, name, bases, dct):
validations = [Validation(f._validation_field_name, f._validation_code, f)
for f in dct.values if hasattr(f._validation_field_name)]
dct["_validations"] = validations
super(ValidatedMeta, meta).__new__(meta, name, bases, dct)
def apply_validation(field_name, code):
def decorator(f):
f._validation_field_name = field_name
f._validation_code = code
return f
return decorator
def ValidatedObject(metaclass=ValidatedMeta):
pass
class test(ValidatedObject):
#apply_validation("_name", "oh no!")
def name_validation(self, name):
return name == "jacob"
After this code runs, test._validations will be [Validation("_name", "oh no!", test.name_validation)]. Note that the method that is be passed to Validation is unbound, so you'll need to pass it a self argument yourself when you call it (or perhaps drop the self argument and change the decorator created in apply_validation to return staticmethod(f)).
This code may not do what you want if you have validation methods defined at several levels of an inheritance hierarchy. The metaclass as written above only checks the immediate class's dict for methods with the appropriate attributes. If you need it include inherited methods in _validations too, you may need to modify the logic in ValidatedMeta.__new__. Probably the easiest way to go is to look for _validations attributes in the bases and concatenate the lists together.
Just an example for using decorators on class method:
from functools import wraps
def VALIDATE(dec):
#wraps(dec)
def _apply_validation(self, name):
self.validate(name)
return dec(self, name)
return _apply_validation
class A:
def validate(self, name):
if name != "aamir":
raise Exception, 'Invalid name "%s"' % name
class B(A):
#VALIDATE
def name_validation(self, name):
return name
b = B()
b.name_validation('jacob') # should raise exception

What's the cleanest way to add a decorator from a derived class to its base class?

I have a set of different classes that share most of their functionality. Their differences can be isolated in a single method and in the decorator to be applied to one of the base methods.
What's the cleanest for the derived classes to set this decorator, which has to be applied to a base class method? I tried something along these lines, but it didn't work, as the method to be decorated was already bound:
class Base(other):
decorator = lambda x:x
def __init__(self, *args, **kwargs):
self.post = self.decorator(self.post)
super(Base, self).__init__(*args, **kwargs)
def post(self):
pass
class Derived(Base):
decorator = some_decorator
The short version is: What you want here is effectively the same thing as a static method, and that's the easiest way to solve it.
The problem isn't that the method, self.post, is bound, but that the decorator, self.decorator, is.
When you store a function as a class attribute, that's basically the same thing as defining a new method. So accessing it as self.decorator is going to get you a bound method. (If you don't understand why, either read the Descriptor HowTo, or take it on faith.) Which means it will be called with that self as its first argument.
You could always add an explicit self parameter to decorator and just ignore it… but if you want a method without a self parameter, that's exactly what a static method is: something that, when used as a method, doesn't take a magic self. So:
class Derived(Base):
#staticmethod
def decorator(func):
whatever(fund)
… or:
class Derived(Base):
decorator = staticmethod(whatever)
If you really want to look up decorator as a data attribute even though it's a function, the easy way is to move it to the instance:
class Derived(Base):
def __init__(self, *args, **kwargs):
self.decorator = whatever
super(Derived, self).__init__(*args, **kwargs)
Or, of course, you can reverse the descriptory methodness:
self.post = self.decorator.im_func(self.post)
… or just avoid it by doing the lookup manually:
decorator = type(self).__dict__['decorator']
self.post = decorator(self.post)
These are all hacky, but then you're trying to do something hacky, so I don't think it's a problem that the hackiness is explicit.

Categories

Resources