Частичный объект функции не имеет атрибута "__code__" - PullRequest
1 голос
/ 04 июля 2019

Я пишу небольшое приложение, которое принимает данные пользователей, чтобы дать им набор оптимальных параметров для использования. (Каждый из этих наборов ранжируется, и пользователь может выбрать тот, который он хочет использовать)

Чтобы сделать это, я выбираю одну функцию из массива вариантов (в зависимости от контекста), частично заполняю функцию, используя functools.partial, затем возвращаю частичный объект другому модулю, который, в свою очередь, вызывает библиотеку C ++ ( dlib), который имеет интерфейс Python.

До сегодняшнего дня я не использовал functools.partial для заполнения функции и не сталкивался с проблемами. Но чтобы сделать код менее повторяющимся и более легким для понимания, я добавил это. После добавления этой части я получаю следующую ошибку:

AttributeError: у объекта 'functools.partial' нет атрибута '__code __'

Я прочитал несколько постов и понял, что это проблема с partial объектами, поскольку у них часто отсутствуют такие атрибуты, как __name__, __module__ и т. Д., Но я не уверен, как решить эту проблему.

PS: я использую python 3.7

EDIT

Я добавляю небольшой код, который воспроизводит ошибку

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,)

Запуск приведенного выше кода приводит к следующей ошибке

AttributeError: у объекта 'functools.partial' нет атрибута '__code __'

Желательно ли вручную добавить атрибут __code__ к частичному объекту?

Ответы [ 2 ]

1 голос
/ 04 июля 2019

Я бы не посмел добавить атрибут __code__ к объекту partial. Атрибут __code__ обеспечивает низкоуровневый доступ к скомпилированному коду Python . Обычно он никогда не используется в общих сценариях и, вероятно, используется здесь для сопряжения его с базовой библиотекой C ++.

Пуленепробиваемый способ - определить новую функцию. В Python def является исполняемым оператором, и можно многократно переопределять функцию:

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 теперь является истинной функцией Python и будет иметь собственный атрибут __code__.


Если библиотека dlib поддерживает это, можно использовать lambda:

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

Лямбда - почти истинная функция и действительно имеет атрибут __code__, но в Python она определена как отдельный объектный класс, поэтому в зависимости от требований библиотеки dlib (я не могу найти на нее никаких ссылок) она может работать или нет.

1 голос
/ 04 июля 2019
AttributeError: 'functools.partial' object has no attribute '__code__'

для решения этой ошибки мы можем использовать

обертывания

functools.WRAPPER_ASSIGNMENTS для обновления атрибутов, по умолчанию (' module ', ' name ', ' doc ') в питоне 2.7.6 или

мы можем обновить только существующие атрибуты ...

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)()

>>> `

`

просто отфильтруйте все те атрибуты, которых нет.

второй подход будет: -

* строго иметь дело только с частичными объектами.

сворачивает обертки с помощью singledispatch, и он создает обернутый частичный объект, а его атрибут будет взят из атрибута функции "fun".

импортные фунтоолы

  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'



 >>>

теперь мы можем получить оригинальную документацию по функциям.

Python (CPython) 3.

из импортных оболочек functools, частичное, WRAPPER_ASSIGNMENTS

попробовать:

 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)))

* мы определяем новую функцию обтеканий, только если нам не удается обернуть частичное, * * используйте оригинальную упаковку для копирования документов *

также

В (Python 3.5) у нас есть ссылка на исходную функцию, и она поддерживается в частичном. Вы можете получить к нему доступ как .func:

из functools частичный импорт

 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'
...