Кажется, что вы действительно хотели написать:
assert item.price == 19.99
(Конечно, сравнение float для равенства или использование float для цен - это плохая идея , поэтому вы напишете assert item.price == Decimal(19.99)
или какой-нибудь цифровой класс, который вы использовали для цены.)
Вы также можете использовать платформу тестирования, например py.test , чтобы получить дополнительную информацию о сбоях утверждает в ваших тестах.
Объявление функции в протоколе дает указание компилятору использовать динамическую отправку при вызове функции, поскольку компилятор ожидает, что типы, реализующие протокол, предоставят реализацию для этой функции. Это называется method requirement
. Если тип не определяет метод, то среда выполнения разрешает вызов метода методу, объявленному в расширении протокола.
Объявление функции только в расширении протокола сообщает компилятору, что он не нужен использовать динамическую отправку и использовать статическую диспетчеризацию, которая выполняется быстрее, но не очень хорошо работает с полиморфизмом, поскольку будет реализована реализация расширения протокола.
Чтобы проиллюстрировать выше, рассмотрим следующий код:
protocol Shape {
func draw()
}
extension Shape {
func draw(){
print("This is a Shape")
}
}
struct Circle: Shape {
func draw() {
print("This is a Circle")
}
}
struct Square: Shape {
func draw() {
print("This is a Square")
}
}
let shapes: [Shape] = [Circle(), Square()]
for shape in shapes {
shape.draw()
}
Вышеприведенный код будет иметь выход
This is a Circle
This is a Square
Это связано с тем, что draw()
является method requirement
, что означает, что при вызове draw()
, среда выполнения будет искать реализацию draw ()
в действительном типе элемента, в данном случае внутри Circle
и Square
.
Теперь, если мы не объявим draw
как требование метода, что означает, что мы не упоминаем его в объявлении протокола
protocol Shape {
}
. Затем компилятор больше не будет использовать динамическую отправку и перейдет прямо к реализации, определенной в расширение протокола. Таким образом, код будет печатать:
This is a Shape
This is a Shape
Подробнее, если мы сбрасываем листинг элемента массива на тип, который, как мы ожидаем, он будет, мы получим статическое связывание назад. Это напечатает This is a Circle
if let circle = shapes[0] as? Circle {
circle.draw()
}
, потому что компилятор теперь может сказать, что первый элемент из shapes
является Circle
, а поскольку Circle
имеет метод draw()
, он будет вызывать это.
Это способ Swift справиться с абстрактными классами: он дает вам возможность указать, что вы ожидаете от типов, соответствующих этому протоколу, и при этом допускать реализацию по умолчанию этих методов.