0

So in python's docutils package there is a class (Image) that has a method (align). As I understood it methods take self as a first argument unless they are decorated as @classmethod or @staticmethod, however align doesn't. The relevant code is copied below (full code here).

class Image(Directive): def align(argument): # This is not callable as self.align. We cannot make it a # staticmethod because we're saving an unbound method in # option_spec below. return directives.choice(argument, Image.align_values) 

I'm using this code as a base for my own purposes, and I have tried both giving align a self argument and turning it into a static method (after changing the name so as not to conflict with self.align), but got errors with either approach. What is going on?

4
  • Could you provide what error are you getting? Commented Sep 26, 2017 at 10:49
  • with @staticmethod: 'staticmethod' object is not callable. With added 'self': align_option() missing 1 required positional argument: 'arg'. Commented Sep 26, 2017 at 10:56
  • Actually this is documented : "This is not callable as self.align. We cannot make it a staticmethod because we're saving an unbound method in option_spec below." Commented Sep 26, 2017 at 11:47
  • The documentation isn't very clear, though. I read that, and my first thought was "Why are you defining the function if it isn't a method?" It took me a while to realize that the function (at least, as accessed via the class attribute) wasn't intended to be used outside the class. Commented Sep 26, 2017 at 11:56

1 Answer 1

2

There is no requirement to name the first argument self; that is just a convention. In the following code

i = Image() i.align() 

the align method will be called with the parameter argument referencing to the object i.

The following function would behave identically:

def align(self): return directives.choice(self, Image.align_values) 

(simply replacing argument with the more common self).


In context, the function align was never intended to be a method; the author appears to simply be defining a function to store in the options_spec dictionary. The desired effect could have been achieved without polluting the class's namespace by deleting the name after the function reference was saved:

option_spec = {'alt': directives.unchanged, 'height': directives.length_or_unitless, 'width': directives.length_or_percentage_or_unitless, 'scale': directives.percentage, 'align': align, 'name': directives.unchanged, 'target': directives.unchanged_required, 'class': directives.class_option} del align 

or by forgoing the def statement—it's a very simple function—and using a lambda expression to create the function.

option_spec = {'alt': directives.unchanged, 'height': directives.length_or_unitless, 'width': directives.length_or_percentage_or_unitless, 'scale': directives.percentage, 'align': lambda x: directives.choice(x, Image.align_values) 'name': directives.unchanged, 'target': directives.unchanged_required, 'class': directives.class_option} 
Sign up to request clarification or add additional context in comments.

5 Comments

That's what I thought, but, at least as I understand the code, what is being passed to align IS a real argument (specifically, whatever the user inputed for the align option) rather than just self with a different name. The directives.choice function checks to see if argument 1 is a member of the the tupple argument 2. Since, Image.align_values is a tupple of strings this means that argument 1 must be a string rather than an instance of class Image.
Ah, it is technically an instance method, but it is never intended to be used as such. Instead, the function is stored in a dict (option_spec), where is is only ever used as a regular function. Something like i.option_spec['align'](foo).
It would probably have been a good idea to add del align after saving the reference in the dict so that the name is removed from the body of the class statement, preventing it from being stored as an attribute of the class. Then there wouldn't be a pseudo-method named align.
Put yet another way, the author should have marked it as a static method if it were intended to be accessed via the class.
Thanks for adding the edit. I'm still a little confused, but I kind of get it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.