Partial function object has no attribute “__code__

2019-07-22 12:12发布

I am writing a small application that takes users' input to give them a set of optimal parameters to use. (Each of these sets are ranked and the user can select whichever one they want to use)

To be able to do this, I select one function from an array of choices(depending on the context), partially fill the function using functools.partial then return the partial object to another module which in turn calls a C++ library (dlib) which has a python interface.

Up until today, I wasn't using functools.partial to fill the function and faced no issues. But to make the code less repetitive and easier to understand, I added that in. After adding that part, I get the following error :

AttributeError: 'functools.partial' object has no attribute '__code__'

I read a few posts and realized that this is an issue with partial objects as they often lack attributes like __name__, __module__ etc but I am not sure how to resolve this issue.

PS: I am using python 3.7

EDIT

I am adding a small code that reproduces the error

from functools import partial
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.datasets import load_breast_cancer
from dlib import find_max_global

def objective_calculator(*args, X, y):
    args = args[0]
    model = LogisticRegression()
    model.set_params(**{'class_weight': args[0], 'max_iter':args[1]})
    model.fit(train['data'], train['target'])
    predictions = model.predict(X)
    return accuracy_score(y, predictions)

train = load_breast_cancer()
obj_calc = partial(objective_calculator, X=train['data'], y=train['target'])
lower_bounds = [0.1, 10] # class_weight, max_iter
upper_bounds = [0.5, 200] # class_weight, max_iter
is_integer_variable = [False, True]

find_max_global(f=obj_calc,
                bound1=lower_bounds,
                bound2=upper_bounds,
                num_function_calls=2,
                is_integer_variable=is_integer_variable,
                solver_epsilon=1,)

Running the above code results in the following error

AttributeError: 'functools.partial' object has no attribute '__code__'

Is it advisable to manually add the __code__ attribute to the partial object?

2条回答
倾城 Initia
2楼-- · 2019-07-22 12:32

I would not dare adding a __code__ attribute to a partial object. The __code__ attribute allows a low level access to the compiled Python code. It is normally never used in common scripts and is probably used here to interface it with the underlying C++ library.

The bullet proof way is to define a new function. In Python def is an executable statement, and it is possible to iterately redefine a function:

def objective_calculator(*args, X, y):
    ...

for X, y in ...:
    def obj_calc(*args):
        return objective_calculator(*args, X, y)
    ...
    find_max_global(f=obj_calc, ...)

obj_calc is now a true Python function and it will have its own __code__ attribute.


If the dlib library supports it, it could be possible to use a lambda:

find_max_global(f=lambda *args: objective_calculator(*args, X, y), ...)

A lambda is almost a true function and has indeed a __code__ attribute, but it is defined as a separate object class in Python, so depending on the dlib library requirements (I could not find any reference on it) it could work or not.

查看更多
贪生不怕死
3楼-- · 2019-07-22 12:35
AttributeError: 'functools.partial' object has no attribute '__code__'

for solving this error we can use

wraps

functools.WRAPPER_ASSIGNMENTS to update attributes, which defaults to ('module', 'name', 'doc') in python 2.7.6 or,

we can update only present attributes...

import functools

import itertools

def wraps_safely(obj, attr_names=functools.WRAPPER_ASSIGNMENTS):
return wraps(obj, assigned=itertools.ifilter(functools.partial(hasattr, obj), attr_names))

`>>> def foo():
`... `   ` """ Ubiquitous foo function ...."""`
... 

>>> functools.wraps(partial(foo))(foo)()

`Traceback (most recent call last):



File "<stdin>", line 1, in <module>



 File ```"/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.py", ``line 33, in update_wrapper`

setattr(wrapper, attr, getattr(wrapped, attr))

AttributeError: 'functools.partial' object has no attribute '__module__'

>>> wraps_safely(partial(foo))(foo)()

>>> `

`

just filter out all those attribute which aren't present.

second approach would be:-

*strictly deal with partial objects only.

fold wraps with singledispatch and it creates wrapped partial object and its attribute would be taken from the function "fun" attribute.

import functools

  def wraps_partial(wrapper, *args, **kwargs):
`    """ Creates a callable object whose attributes will be set from the partials nested func attribute ..."""

 ` ` ` wrapper = wrapper.func``



 while isinstance(wrapper, functools.partial):


 wrapper = wrapper.func


 return functools.wraps(wrapper, *args, **kwargs)

# after returning functools.wraps

def foo():
""" Foo function.
:return: None """


 pass

>>> wraps_partial(partial(partial(foo)))(lambda : None).__doc__

' Foo Function, returns None '

>>> wraps_partial(partial(partial(foo)))(lambda : None).__name__

'foo'

>>> wraps_partial(partial(partial(foo)))(lambda : None)()

>>> pfoo = partial(partial(foo))



 >>> @wraps_partial(pfoo)



 ... def not_foo():


 ... 




 """ Not Foo function ... """


    ... 

 >>> not_foo.__doc__



 ' Foo Function, returns None '

>>> not_foo.__name__

'foo'



 >>>

now we can get the original functions docs.

python(CPython) 3.

from functools import wraps, partial, WRAPPER_ASSIGNMENTS

try:

 wraps(partial(wraps))(wraps)



 except AttributeError:



  @wraps(wraps)


 def wraps(obj, attr_names=WRAPPER_ASSIGNMENTS, wraps=wraps):


 return wraps(obj, assigned=(name for name in attr_names if hasattr(obj, name)))

*we define a new wraps function only if we fail to wrap a partial,* *use the original wraps to copy the docs*

also,

In (Python 3.5) we have a reference to the original function, and it is maintained in the partial. You can access it as .func:

from functoolsimport partial

 def a(b):
print(b)


In[20]:  c=partial(a,5)


In[21]:  c.func.__module__

Out[21]: '__main__'

In[22]:  c.func.__name__

Out[22]: 'a'
查看更多
登录 后发表回答