6đ
funny that you should ask about this, I was just a bit surprised by a different use of this in the django documentation earlier today:
def upper_case_name(obj):
return ("%s %s" % (obj.first_name, obj.last_name)).upper()
upper_case_name.short_description = 'Name'
# !!! another occurrence of a "method attribute," an attribute
# assigned to a function object.
class PersonAdmin(admin.ModelAdmin):
list_display = (upper_case_name,)
So, what this means, essentially, is that function definitions are a type of object. A more familiar way of saying this might be:
>>> def myfunc():
... return "myvalue"
# 'myfunc' is now an object of type 'function' in the local scope. observe:
>>> type(myfunc)
<type: 'function'>
# you can, of course call __call__ on 'myfunc':
>>> myfunc()
"myvalue"
>>> myfunc.__call__()
"myvalue"
# and because 'myfunc' is also a normal object, you can define attributes on it.
myfunc.someattribute = 'somevalue'
myfunc.is_a_function = True
myfunc.takes_args = False
So, your question has a bit to do with the idea that python is âobjects all the way down,â that is, that everything in python is an object.
Now why is this useful? Suppose you want to collect and use some metadata on a set of functions (or methods) that youâre writing:
from operator import attrgetter
def add(x, y):
return x + y
def subtract(x, y):
return x - y
def get_attribute_value(obj, attr):
return attrgetter(attr)(obj)
add.takes_args = True
add.number_of_args = 2
add.type_of_args = [int, int]
add.uses_black_magic = False
subtract.takes_args = True
subtract.number_of_args = 2
subtract.type_of_args = [int, int]
subtract.uses_black_magic = False
get_attribute_value.takes_args = True
get_attribute_value.number_of_args = 2
get_attribute_value.type_of_args = [object, str]
get_attribute_value.uses_black_magic = True
You could then use these âmethod attributesâ in a useful way:
def perform_function_checks(function_list):
for afunc in function_list:
if getattr(afunc, 'takes_args'):
print "function '%s' takes args! how unusual!" % (afunc.__name__,)
if getattr(afunc, 'number_of_args'):
print "function '%s' takes %s args." % (afunc.__name__, afunc.number_of_args)
if getattr(afunc, 'type_of_args'):
print "function '%s' takes %s args: %s" (afunc.__name__, len(afunc.type_of_args), [", and ".join(str(item)) for item in afunc.type_of_args])
if getattr(afunc, 'uses_black_magic'):
print "oh no! function '%s' uses black magic!" % (afunc.__name__,)
perform_function_checks([add, subtract, get_attribute_value])
# prints:
# function 'add' takes args! how unusual!
# function 'add' takes 2 args.
# function 'add' takes 2 args: <type 'int'>, and <type 'int'>
# function 'subtract' takes args! how unusual!
# function 'subtract' takes 2 args.
# function 'subtract' takes 2 args: <type 'int'>, and <type 'int'>
# function 'get_attribute_value' takes args! how unusual!
# function 'get_attribute_value' takes 2 args.
# function 'get_attribute_value' takes 2 args: <type 'object'>, and <type 'str'>
# oh no! function 'get_attribute_value' uses black magic!
Now, of course, the above is for illustrative purposes only, if you were actually trying to do this type of introspection on functions and objects youâd probably want to use the âinspectâ module, rather than adding your own bizarro metadata: http://docs.python.org/library/inspect.html
For more information on this topic, Iâd recommend this post:
http://www.cafepy.com/article/python_types_and_objects/python_types_and_objects.html
â EDIT:
sorry, I didnât address your âimplementation that allows method attributes to be callableâ under addendum #2.
Your example there is a bit of a red herring in this discussion. Whatâs going on there, is that someone is using the @property decorator to decorate a method to make it look like a property (a.k.a. âattributeâ). Consider this example:
# let's define a simple class
class Foo():
# and a "normal" attribute
an_attribute = 'a value'
# now a method that we'll decorate with the @property decorator
@property
def what_kind(self):
return str(self.__class__.__name__)
# now, instances of our Foo class will have the attribute '.what_kind'.
>>> bar = Foo()
# bar is now an instance of foo.
>>> bar.an_attribute
"a value"
# and finally our decorated method:
>>> bar.what_kind
"Foo"
Note that we did not have to call âwhat_kindâ above to return the value. I think that all the @property decorator does is automatically call .__call__()
so, what the author of that post is doing is making it look to django that youâre just adding a âplain oldâ attribute to the class, when, in fact, .short_description and .votes_today are actually methods.
Hereâs more information on the @property decorator/function (which is builtin, BTW, so you donât need to import it):
http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/
â EDIT: fixed a couple of markup problems, and a typo.
-6đ
This question is pretty far-reaching if Iâm understanding it correctly. Depending on the object you are looking at there are likely a lot of methods and attributes.
I recommend:
- Look at the source. Djangoâs docs are a great starting point but youâll find itâs much more powerful than what is openly described.
-
Get yourself setup for tab completion and open a Django shell. Then you can tab around to see what methods and attributes are available.
python manage.py shell
- Improving Performance of Django ForeignKey Fields in Admin
- How to test (using unittest) the HTML output of a Django view?
- Django cms â invalid block tag endblock