Защитное утверждение работает следующим образом: если первый предикат равен false
, он not
оценивает следующий предикат (ы).
Другими словами, вы можете считать запятую ,
оператора guard такой же, как оператор &&
. Это то, что мы называем ленивой оценкой; Если вы проверили, как &&
работает , вы заметите, что rhs
представляет собой автозаполнение возвращает логическое значение, но не логическое значение, что означает, что оно может даже не оцениваться (в случае lhs
параметр равен false
).
хотите узнать больше об автозаполнении? проверьте это !
Итак, для вашего случая вы можете объединить все предикаты (которые, я думаю, будут более читабельными ИМО) в одном выражении guard
. [1117 ]
Python строго типизирован, потому что каждый объект имеет тип, каждый объект знает свой тип, невозможно случайно или намеренно использовать объект типа «как если бы» это был объект другого типа, и все элементарные операции с объектом делегируются его типу.
Это не имеет ничего общего с именами . Имя в Python не «имеет тип»: если и когда имя определено, имя относится к объекту , а объект действительно имеет тип (но на самом деле это не заставляет тип использовать имя : имя есть имя).
Имя в Python может прекрасно относиться к различным объектам в разное время (как и в большинстве языков программирования, хотя и не на всех) - и нет ограничений на имя, например, если оно однажды ссылалось на объект типа X, тогда он навсегда ограничен ссылаться только на другие объекты типа X. Ограничения на имен не являются частью концепции «строгой типизации», хотя некоторые энтузиасты статической типизации (где имена действительно ограничиваются, и в статике, также как и во время компиляции), таким образом неправильно используют термин.
Я реализовал обертку, если кто-либо хотел бы указать типы переменных.
import functools
def type_check(func):
@functools.wraps(func)
def check(*args, **kwargs):
for i in range(len(args)):
v = args[i]
v_name = list(func.__annotations__.keys())[i]
v_type = list(func.__annotations__.values())[i]
error_msg = 'Variable `' + str(v_name) + '` should be type ('
error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ')'
if not isinstance(v, v_type):
raise TypeError(error_msg)
result = func(*args, **kwargs)
v = result
v_name = 'return'
v_type = func.__annotations__['return']
error_msg = 'Variable `' + str(v_name) + '` should be type ('
error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ')'
if not isinstance(v, v_type):
raise TypeError(error_msg)
return result
return check
Использование это как:
@type_check
def test(name : str) -> float:
return 3.0
@type_check
def test2(name : str) -> str:
return 3.0
>> test('asd')
>> 3.0
>> test(42)
>> TypeError: Variable `name` should be type (<class 'str'>) but instead is type (<class 'int'>)
>> test2('asd')
>> TypeError: Variable `return` should be type (<class 'str'>) but instead is type (<class 'float'>)
код выше не работает, если какой-либо из аргументов (или возврат) тип не объявляется. Следующее редактирование может помочь, с другой стороны, оно только работает на kwargs и не проверяет args.
def type_check(func):
@functools.wraps(func)
def check(*args, **kwargs):
for name, value in kwargs.items():
v = value
v_name = name
if name not in func.__annotations__:
continue
v_type = func.__annotations__[name]
error_msg = 'Variable `' + str(v_name) + '` should be type ('
error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ') '
if not isinstance(v, v_type):
raise TypeError(error_msg)
result = func(*args, **kwargs)
if 'return' in func.__annotations__:
v = result
v_name = 'return'
v_type = func.__annotations__['return']
error_msg = 'Variable `' + str(v_name) + '` should be type ('
error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ')'
if not isinstance(v, v_type):
raise TypeError(error_msg)
return result
return check
Как Алекс Мартелли объясняет ,
Нормальное, питоническое, предпочтительное решение почти всегда - "утиный ввод": попробуйте использовать аргумент, как если бы он был определенного желаемого типа, сделайте это в операторе try / except, перехватывая все исключения, которые могли бы возникнуть, если бы аргумент не был на самом деле этого типа (или любого другого типа, красиво имитирующего его ;-), и в предложении except попробуйте что-нибудь еще (используя аргумент «как если бы» он был другого типа).
Прочтите остальную часть его сообщения для получения полезной информации.
Python не волнует, что вы передаете его функциям. Когда вы вызываете my_func (a, b)
, переменные param1 и param2 будут содержать значения a и b. Python не знает, что вы вызываете функцию с правильными типами, и ожидает, что программист позаботится об этом. Если ваша функция будет вызываться с разными типами параметров, вы можете обернуть код, обращающийся к ним, с помощью блоков try / except и оценивать параметры любым способом.
Python не является строго типизированным в смысле статической проверки типов или проверки типов во время компиляции.
Большая часть кода Python подпадает под так называемый «Duck Typing» - например, вы ищете метод read
для объекта - вам все равно, если объект - это файл на диске или в сокете, вы просто хотите прочитать из него N байтов.
Вы не указали тип. Метод потерпит неудачу (во время выполнения), только если попытается получить доступ к атрибутам, которые не определены для переданных параметров.
Поэтому эта простая функция:
def no_op(param1, param2):
pass
... не потерпит неудачу независимо от того, какие два аргумента будут переданы.
Однако эта функция:
def call_quack(param1, param2):
param1.quack()
param2.quack()
... потерпит неудачу во время выполнения, если param1
и param2
не имеют вызываемых атрибутов с именем quack
.
Вы никогда не указываете тип; в Python есть концепция утиной типизации; в основном код, обрабатывающий параметры, делает определенные предположения о них - возможно, вызывая определенные методы, которые параметр должен реализовать. Если параметр имеет неправильный тип, то будет выброшено исключение.
В общем, именно ваш код должен убедиться, что вы передаете объекты нужного типа - нет компилятора, который бы обеспечил это заранее.
В Python все имеет тип. Функция Python будет делать все, что от нее требуется, если тип аргументов это поддерживает.
Пример: foo
добавит все, что можно __ add __
ed;), не беспокоясь о его типе. Это означает, что во избежание неудач вы должны предоставлять только те вещи, которые поддерживают добавление.
def foo(a,b):
return a + b
class Bar(object):
pass
class Zoo(object):
def __add__(self, other):
return 'zoom'
if __name__=='__main__':
print foo(1, 2)
print foo('james', 'bond')
print foo(Zoo(), Zoo())
print foo(Bar(), Bar()) # Should fail
Во многих языках есть переменные определенного типа и значения. Python не имеет переменных; у него есть объекты, и вы используете имена для ссылки на эти объекты.
В других языках, когда вы говорите:
a = 1
, тогда (обычно целочисленная) переменная меняет свое содержимое на значение 1.
В Python
a = 1
означает «использовать имя a для ссылки на объект 1 ». В интерактивном сеансе Python вы можете сделать следующее:
>>> type(1)
<type 'int'>
Функция типа
вызывается с объектом 1
; так как каждый объект знает свой тип, для типа
легко найти указанный тип и вернуть его.
Аналогичным образом, всякий раз, когда вы определяете функцию
def funcname(param1, param2):
, функция получает два объекта и называет их param1
и param2
, независимо от их типов. Если вы хотите убедиться, что полученные объекты относятся к определенному типу, запрограммируйте свою функцию так, как если бы они имеют требуемый тип (ы), и перехватите исключения, которые будут выброшены, если они не являются. Вызываемые исключения обычно: TypeError
(вы использовали недопустимую операцию) и AttributeError
(вы пытались получить доступ к несуществующему члену (методы также являются членами)).