class SessionWizardView(WizardView):
#classonlymethod
def as_view(cls, *args, **kwargs):
#...snipped..
pass
class ParentWizard(SessionWizardView):
#classonlymethod
def as_view(cls, *args, **kwargs):
return super(SessionWizardView, cls).as_view( ... )
class ChildWizard(ParentWizard):
#classonlymethod
def as_view(cls, *args, **kwargs):
return super(SessionWizardView, cls).as_view( ... )
In ChildWizard, is it legal to pass in a grandparent class (SessionWizardView) into the first parameter of super ? pylint is vomiting this error message:
Method should have "self" as first argument
Bad first argument 'SessionWizardView' given to super class
The usual usage of super for classmethods is to put cls first and the name of the current class as the second argument:
class SessionWizardView(WizardView):
#classonlymethod
def as_view(cls, *args, **kwargs):
#...snipped..
pass
class ParentWizard(SessionWizardView):
#classonlymethod
def as_view(cls, *args, **kwargs):
return super(cls, ParentWizard).as_view( ... )
class ChildWizard(ParentWizard):
#classonlymethod
def as_view(cls, *args, **kwargs):
return super(cls, ChildWizard).as_view( ... )
If you build working code that passes tests, I wouldn't worry to much about how pylint reports the naming of the first argument. pylint knows that a standard Python classmethod will use cls as the first argument, but it doesn't know anything about classonlymethod which has the same pattern.
References:
http://docs.python.org/library/functions.html#super
http://blogs.gnome.org/jamesh/2005/06/23/overriding-class-methods-in-python/
http://pylint-messages.wikidot.com/messages:c0202
Related
I'd like to create a custom Python decorator that will 1) check the value of a class attribute my_attribute before running the function, and 2) turn the class method into a property. I can achieve this as follows:
def my_decorator(f):
def wrapper(self, *args, **kwargs):
if self.my_attribute is None:
raise ValueError('No location set to model.')
return f(self, *args, **kwargs)
return wrapper
class MyClass:
my_attribute = None
#property
#my_decorator
def my_method():
return self. my_attribute
I'd like to know how I can edit the definition of my_decorator so that it makes the wrapped method a property. Essentially I'd like to avoid the use of #property and #my_decorator for each method, letting me write the class definition as
class MyClass:
my_attribute = None
#my_new_decorator
def my_method():
return self. my_attribute
I've looked up the declaration of the builtin #property decorator, but it's defined as a class and wasn't much help.
Any suggestions?
What if you changed your decorator definition to look like this?
def my_decorator(f):
#property
def wrapper(self, *args, **kwargs):
if self.my_attribute is None:
raise ValueError('No location set to model.')
return f(self, *args, **kwargs)
return wrapper
That should make the returned function behave like a property, and keep your custom error handling.
If one wants to keep/use also the original my_decorator, one could create a my_decorator_property as following:
def my_decorator_property(f):
#property
def wrapper(self, *args, **kwargs):
return my_decorator(f)(self, *args, **kwargs)
return wrapper
In Python 3.x with the given class hierarchy, where SubClassZero and SubClassOne inherit from SuperClass;
class SuperClass(object):
def __init__(self, *args, **kwargs):
self.thing_zero = "Inherited string."
class SubClassZero(SuperClass):
def __init__(self, *args, **kwargs):
SuperClass.__init__(self, *args, **kwargs)
self.thing_one = "SubClassZero's string."
class SubClassOne(SuperClass):
def __init__(self, *args, **kwargs):
SuperClass.__init__(self, *args, **kwargs)
self.thing_one = "SubClassOne's string."
scz = SubClassZero()
sco = SubClassOne()
If we want to give the subclasses the ability set a title from a kwarg then the __init__ function of SuperClass can be redefined using the the built setattr function like so;
def __init__(self, *args, **kwargs):
self.thing_zero = "Inherited string."
if 'title' in kwargs:
self.title = kwargs['title']
else:
self.title = ""
setattr(SuperClass, '__init__', __init__)
But to do that one has to know the previous structure of __init__ to maintain full functionality. Is there a way to somehow add to the __init__ function of SuperClass without completely overwriting it?
I have the following classes.
Validator is a decorator that receives a class which defines validation criteria for a decorated function. ValidateKeys is the validation criteria for this example. Node2D is a class using validation criteria.
class Validator(object):
def __init__(self, TheValidator, *args, **kwargs):
self.validator = TheValidator(*args,**kwargs)
def __call__(self,f):
def wrapped_f(instance, *args,**kwargs):
self.TheValidator(instance, *args, **kwargs)
return f(instance,*args,**kwargs)
return wrapped_f
class ValidateKeys(object):
def __init__(self,*keysIterable):
self.validkeys = keysIterable
def __call__(self, instance, **kwargs):
for a in kwargs:
if not a in self.validkeys:
raise Exception()
instance.__dict__.update(kwargs)
class Node2D(object):
#property
def coords(self):
return self.__dict__
#coords.setter
def coords(self,Coords):
self.set_coords(**Coords)
#Validator(ValidateKeys, 'x','y')
def set_coords(self,**Coords):
pass
From what I understand, as things are written here, every instance of Node2D will produce a duplicate Validator (as will any other class decorated with Validator) and ValidateKeys.
EDIT: THIS IS WRONG! See answer below.
Note that this is primarily a learning exercise for me and although I would be interested in hearing criticisms/suggestions for improving my over all approach, my primary goal is to learn more about how to use decorators effectively.
Also note that I normally would not use capitalization for a decorator class but am using it here since it makes it easier to read on SO.
My assumption was incorrect.
As things are written, only one instance of Validator and ValidateKeys is created per class. I did not realize that the line #Validator(ValidateKeys, 'x','y') only runs once (at the time of class definition) and not at instance creation.
I should have realized this, since decorator expressions appear at the same level of hierarchy as class attributes, e.g.:
class MyClass():
class_attribute = None #only one class_attribute is created
#decorator #only one decorator (i.e., decorated method) is created
def method():
pass
Test:
class Validator(object):
def __init__(self, TheValidator, *args, **kwargs):
print("New Validator Object")
self.TheValidator = TheValidator(*args,**kwargs)
def __call__(self,f):
def wrapped_f(instance, *args,**kwargs):
self.TheValidator(instance, *args, **kwargs)
return f(instance,*args,**kwargs)
return wrapped_f
class ValidateKeys(object):
def __init__(self,*keysIterable):
print("New ValidateKeys Object")
self.validkeys = keysIterable
def __call__(self, instance, **kwargs):
for a in kwargs:
if not a in self.validkeys:
raise Exception()
instance.__dict__.update(kwargs)
class Node2D(object):
#property
def coords(self):
return self.__dict__
#coords.setter
def coords(self,Coords):
self.set_coords(**Coords)
#Validator(ValidateKeys, 'x','y')
def set_coords(self,**Coords):
pass
n1 = Node2D()
n2 = Node2D()
n1.setcoords(x=1,y=2)
n1.coords
Output:
'New Validator Object' #<-- Seen only once when module is loaded (class defined)
'New ValidateKeys Object' #<-- Seen only once when module is loaded (class defined)
'{'x': 1, 'y': 2}'
I do not have the problem I thought I had. Thanks to all for the help.
I have a decorator declared as a class:
class predicated(object):
def __init__(self, fn):
self.fn = fn
self.fpred = lambda *args, **kwargs: True
def predicate(self, predicate):
self.fpred = predicate
return self
def validate(self, *args, **kwargs):
return self.fpred(*args, **kwargs)
def __call__(self, *args, **kwargs):
if not self.validate(*args, **kwargs):
raise PredicateNotMatchedError("predicate was not matched")
return self.fn(*args, **kwargs)
... and when I use it to wrap a method in a class, calling that method does not seem to set the instance of the object as the first argument. While this behavior is not exactly unexpected, how would I go about getting self to be frozen when the method becomes an instance method?
Simplified example:
class test_decorator(object):
def __init__(self, fn):
self.fn = fn
def __call__(self, *args, **kwargs):
return self.fn(*args, **kwargs)
class Foo(object):
#test_decorator
def some_method(self):
print(self)
Foo().some_method()
Expected instance of foo, instead get an error saying 0 arguments were passed.
Figured it out - needed to define a __get__ method in order to create a MethodType binding like so:
def __get__(self, obj, objtype=None):
return MethodType(self, obj, objtype)
which creates a MethodType object when invoking the method on an object that freezes the self argument.
Here's an example of what I mean:
class MyDecorator(object):
def __call__(self, func):
# At which point would I be able to access the decorated method's parent class's instance?
# In the below example, I would want to access from here: myinstance
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
class SomeClass(object):
##self.name = 'John' #error here
name="John"
#MyDecorator()
def nameprinter(self):
print(self.name)
myinstance = SomeClass()
myinstance.nameprinter()
Do I need to decorate the actual class?
class MyDecorator(object):
def __call__(self, func):
def wrapper(that, *args, **kwargs):
## you can access the "self" of func here through the "that" parameter
## and hence do whatever you want
return func(that, *args, **kwargs)
return wrapper
Please notice in this context that the use of "self" is just a convention, a method just uses the first argument as a reference to the instance object:
class Example:
def __init__(foo, a):
foo.a = a
def method(bar, b):
print bar.a, b
e = Example('hello')
e.method('world')
The self argument is passed as the first argument. Also your MyDecorator is a class emulating a function. Easier to make it an actual function.
def MyDecorator(method):
def wrapper(self, *args, **kwargs):
print 'Self is', self
return method(self, *args, **kwargs)
return wrapper
class SomeClass(object):
#MyDecorator
def f(self):
return 42
print SomeClass().f()