4

Apologies upfront if this is a dupe; I search for "_curried python" and got 14 results, and then simply _curried" and that only bumped up to 33 results, and none seemed to help out...

The problem: I came across what I originally thought was a typo in our codebase today, here is the suspect:

student.recalculate_gpa()

Now, I suspect it to be a typo because student is an instance of a Student class that has no recalculate_gpa method. However, it does have a calculate_gpa method:

class Student(User):
    ...
    def calculate_gpa(self):
        # some calculations...

(Where User is the standard django user class.) But, the code wasn't erroring, which made no sense to me. So I did an inspect and found this:

... (a bunch of methods)
('calculate_gpa', <unbound method Student.calculate_gpa>),
... (some more methods)
('recalculate_gpa', <unbound method Student._curried>),

Strange, recalculate_gpa is in fact a method. But where on earth does it come from? I grep'd for "_curried" in our codebase and found nothing, so this must be some Django-related behavior. Certainly I would expect that somewhere in our project we've described how dynamically named functions are formed since recalculate seems like a plausible derivative of calculate, but I honestly have no idea where to start looking.

Thus, my question: how are curried methods like the one above generated, and where should I start looking to discover how our own codebase is curry-ing?

Thanks a ton in advance!

Quentin Donnellan
  • 2,687
  • 1
  • 18
  • 24

1 Answers1

5

a curried method is when you partially call a method in advance of actually calling it

for example

from functools import partial
from itertools import count
def my_pow(x,y):
    return x**y

curried_pow2n = partial(my_pow,x=2)

for i in count(0): #print the 2**i
    print curried_pow2n(i)

you could also easily implement it with lambda

curried_pow2n = lambda x:return my_pow(2,x)

although Im not sure this has anything to do with your actual question ...

django also provides a curry method that is pretty similar to functools.partial

from django.utils.functional import curry    

lols = {'lols':'lols'}
formset = modelformset_factory(MyModel, form=myForm, extra=0)
formset.form = staticmethod(curry(MyForm, lols=lols))

(from https://stackoverflow.com/a/25915489/541038)

so you may want to look for Student.recalculate_gpa =

or perhaps in the Student.__init__ method for self.recalculate_gpa =

you likely would not find it looking for def recalculate_gpa

Community
  • 1
  • 1
Joran Beasley
  • 110,522
  • 12
  • 160
  • 179
  • Well here's the crazy thing, when I grep for "recalculate_gpa" across the entire project there is exactly one hit: the method call `student.recalculate_gpa()` – Quentin Donnellan Feb 05 '15 at 20:18
  • what if you grep `curry` (there may be some trickery in creating it) (are you sure you grepped the entire project also?) – Joran Beasley Feb 05 '15 at 20:19
  • Only "curry" instances are in our JS files - totally unrelated - ok... time for some tea and regrouping... – Quentin Donnellan Feb 05 '15 at 20:26
  • are you overloading `__getattr__` or something weird like that? I guarantee that django is not magically adding a `recalculate_gpa` method to your class – Joran Beasley Feb 05 '15 at 20:27
  • 7
    Try: `[cell.cell_contents for cell in Student.recalculate_gpa.__closure__]` to see what function has been wrapped and with what arguments. – Dunes Feb 05 '15 at 20:33
  • Oh man that is genious! turns out it was a third party doing the trickery: https://github.com/aquameta/django-cached-field/blob/master/django_cached_field/__init__.py#L123 related to a different field - I'll have to just write up my own answer to explain everything... holy heck what a trace! – Quentin Donnellan Feb 05 '15 at 20:46
  • 1
    I knew for sure that it wasnt magic :P ... @QuentinDonnellan ... careful on posting your own answer if it is too targeted to your very specific problem other people likely wont find it helpful – Joran Beasley Feb 05 '15 at 20:47
  • 1
    Thank this is very helpful! Evryone who is facing ```ImportError: cannot import name 'curry' from 'django.utils.functional'``` this error, you can replace curry method with "partial" from "functools". Just import partial method as mentioned in this answer and replace curry with partial() wherever its getting used. Since django have removed this package in latest version. Partial works exactly the similar way!! – Sunny Chaudhari Dec 19 '20 at 06:10