#python
#питон #python
Вопрос:
Я пытаюсь проверить свое понимание функций-оболочек, поэтому создал этот код
def wrapper():
print ("Wrapper")
def wrapped():
print ("Wrapped")
return wrapped
if int (wrapper()):
print ("Returned integer")
Я попробовал if str(wrapper())
и if callable (wrapper())
, и все было в порядке
Я не понимаю, почему if int (wrapper())
выдает ошибку:
Exception has occurred: TypeError
int() argument must be a string, a bytes-like object or a number, not 'function'
Конечно, он должен просто сказать False и вообще не содержать ошибки?
Комментарии:
1. Что вы пытаетесь сделать с
if int(...)
? Для чего вы тестируете? … docs.python.org/3/library/functions.html#int
Ответ №1:
Я не знаю, что именно вы тестируете, но я могу попытаться объяснить, что делает ваш код.
Случай 1: if str(wrapper())
-
При вызове
wrapper()
он выводит «оболочку», а затем возвращает вызванную функциюwrapped
. -
Таким образом, вызов
str(wrapper())
эквивалентенstr(wrapped)
. Обратите внимание, что после этого нет круглых скобокwrapped
. Это означает, что аргументstr
функции является объектом функции.При вызове
str
объекта функции он возвращает строку с именем функции и положением в памяти. Проверьте этот пример:
>>> def test():
... pass
...
>>> str(test)
'<function test at 0x7fa95c504668>'
>>> type(str(test))
<type 'str'>
- Поэтому
if str(wrapper())
будет вычислятьсяif '<function wrapped at 0x7fa95c504668>'
. - Для всех непустых строк выполняется оператор if. Посмотрите на этот пример:
>>> if "string":
... print("Non empty string")
...
Non empty string
>>> if "":
... print("This line will not be printed")
...
>>>
Случай 2: if callable(wrapper())
-
то же, что и раньше
-
callable
проверяет, имеет ли объект__call__
атрибут -
Вызов
callable(wrapper())
эквивалентенcallable(wrapped)
. Посколькуwrapped
это функция, она имеет__call__
атрибут, а выражение оценивается какTrue
. -
Таким образом,
if callable(wrapper())
вычисляетсяif True
, и оператор if будет выполнен!
Случай 3: if int(wrapper())
-
то же, что и раньше
-
int
может только преобразовывать числа и строки в целые числа. -
int(wrapper())
эквивалентноint(wrapped)
. Итак, вы пытаетесь вызватьint
с функциональным объектом! -
int
не удается преобразовать функциональный объект в целое число и выдает сообщение со стрелкой:
Exception has occurred: TypeError
int() argument must be a string, a bytes-like object or a number, not 'function'
Комментарии:
1. Спасибо за вашу помощь!
Ответ №2:
Декоратор — это просто причудливое имя для функции, которая принимает функцию в качестве аргумента и возвращает функцию. Итак, примером декоратора является:
def wrapper(func):
print ("Wrapper")
def wrapped(*args, **kwargs):
print ("Wrapped")
func(*args, **kwargs)
return wrapped
@wrapper
def to_wrap():
print("decorated function")
который, если вы запустите to_wrap()
, вы на самом деле запускаете wrapped()
с переменной func
, равной to_wrap
.
Итак, имея в виду эту идею, давайте посмотрим на ваш код:
if int (wrapper()):
print ("Returned integer")
то, что wrapper()
возвращает, является функцией (как в вашем коде, так и в моей модификации выше), а затем вы пытаетесь преобразовать это в int
и проверить его логическое значение. Это преобразование не реализовано, и, следовательно, Python вызывает TypeError, поскольку объект типа функции не может быть преобразован в целочисленный тип.
Комментарии:
1. Спасибо за вашу помощь!
Ответ №3:
Декоратор все еще необходимо вызвать. То, что у вас есть прямо сейчас, — это закрытие, которое возвращает функцию:
def f():
def wrapped():
return 10
return wrapped
x = f()
# x is the function 'wrapped'
Обратите внимание, что это не вызывалось wrapped
, оно просто вернуло его. Вам нужно будет вызвать x
, чтобы изменить это поведение:
x()
10
# Which is implicitly calling wrapped() to return 10
Синтаксис декоратора позаботится об этом за вас:
def f(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper # still returns an un-called function
@f
def x():
return 10
x()
# 10
Где @f
будет эффективно делать f(x())
. Поскольку вы не вызывали функцию, вы никогда не получали возвращаемый тип, поэтому int
возникает TypeError
.
В вашем примере явно:
def wrapper():
print("wrapper")
def wrapped():
print("wrapped")
return "1" # you need to return a value here, otherwise it will return None
return wrapped
int(wrapper()())
# wrapper
# wrapped
# 1
Обратите внимание на дополнительные круглые скобки после wrapper
вызова, который вызывает wrapped
. Кроме того, если вы не возвращаете значение из wrapped
, то вы вернетесь None
, что вызовет другое TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'
Комментарии:
1. Спасибо за вашу помощь!