Исключение нулевого указателя генерируется, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:
null
. null
. null
, как если бы это был массив. null
, как если бы это был массив. null
как будто это было значение Throwable. Приложения должны бросать экземпляры этого класса, чтобы указать на другие незаконные использования объекта null
.
Ссылка: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
Используется для отправки значений в генератор, который только что дал. Вот пример (не полезный):
>>> def double_inputs():
... while True:
... x = yield
... yield x * 2
...
>>> gen = double_inputs()
>>> next(gen) # run up to the first yield
>>> gen.send(10) # goes into 'x' variable
20
>>> next(gen) # run up to the next yield
>>> gen.send(6) # goes into 'x' again
12
>>> next(gen) # run up to the next yield
>>> gen.send(94.3) # goes into 'x' again
188.5999999999999
Вы не можете сделать это только с помощью yield
.
Что касается того, почему это полезно, один из лучшие примеры использования, которые я видел, это Twisted's @defer.inlineCallbacks
. По сути, это позволяет вам написать такую функцию:
@defer.inlineCallbacks
def doStuff():
result = yield takesTwoSeconds()
nextResult = yield takesTenSeconds(result * 10)
defer.returnValue(nextResult / 10)
Что происходит, так это то, что takesTwoSeconds()
возвращает Deferred
, что является значением, обещающим, что значение будет вычислено позже. Twisted может выполнять вычисления в другом потоке. Когда вычисление выполняется, оно передает его в отложенное, а затем значение возвращается к функции doStuff()
. Таким образом, doStuff()
может оказаться более или менее похожей на обычную процедурную функцию, за исключением того, что она может выполнять всевозможные вычисления и amp; обратные вызовы и т. д. Альтернативой перед этой функциональностью было бы сделать что-то вроде:
def doStuff():
returnDeferred = defer.Deferred()
def gotNextResult(nextResult):
returnDeferred.callback(nextResult / 10)
def gotResult(result):
takesTenSeconds(result * 10).addCallback(gotNextResult)
takesTwoSeconds().addCallback(gotResult)
return returnDeferred
Это намного сложнее и громоздко.
send()
Генераторы с send()
позволяют:
Вот несколько вариантов использования:
Давайте попробуем рецепт, ожидающий предопределенный набор входов в некотором порядке.
Мы можем:
watched_attempt
из рецепта def recipe():
pot = []
action = yield pot
assert action == ("add", "water")
pot.append(action[1])
action = yield pot
assert action == ("add", "salt")
pot.append(action[1])
action = yield pot
assert action == ("boil", "water")
action = yield pot
assert action == ("add", "pasta")
pot.append(action[1])
action = yield pot
assert action == ("decant", "water")
pot.remove("water")
action = yield pot
assert action == ("serve")
pot = []
yield pot
Чтобы использовать его, сначала создайте экземпляр watched_attempt
:
>>> watched_attempt = recipe()
>>> watched_attempt.next()
[]
Для запуска выполнения генератора необходимо вызвать .next()
.
Возвращаемое значение показывает, что наш банк в настоящее время пуст.
Теперь выполните несколько действий, следуя рецепту бывший pect:
>>> watched_attempt.send(("add", "water"))
['water']
>>> watched_attempt.send(("add", "salt"))
['water', 'salt']
>>> watched_attempt.send(("boil", "water"))
['water', 'salt']
>>> watched_attempt.send(("add", "pasta"))
['water', 'salt', 'pasta']
>>> watched_attempt.send(("decant", "water"))
['salt', 'pasta']
>>> watched_attempt.send(("serve"))
[]
Как мы видим, банк окончательно пуст.
В случае, если вы не будете следовать рецепту, он потерпит неудачу (что может быть желательным результатом просмотра
>>> watched_attempt = running.recipe()
>>> watched_attempt.next()
[]
>>> watched_attempt.send(("add", "water"))
['water']
>>> watched_attempt.send(("add", "pasta"))
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-21-facdf014fe8e> in <module>()
----> 1 watched_attempt.send(("add", "pasta"))
/home/javl/sandbox/stack/send/running.py in recipe()
29
30 action = yield pot
---> 31 assert action == ("add", "salt")
32 pot.append(action[1])
33
AssertionError:
Обратите внимание, что:
Мы можем использовать генератор для отслеживания выполнения общего количества присланных ему значений.
Каждый раз, когда мы добавляем число , возвращается количество входов и суммарная сумма (действителен на тот момент, когда в нее был отправлен предыдущий ввод).
from collections import namedtuple
RunningTotal = namedtuple("RunningTotal", ["n", "total"])
def runningtotals(n=0, total=0):
while True:
delta = yield RunningTotal(n, total)
if delta:
n += 1
total += delta
if __name__ == "__main__":
nums = [9, 8, None, 3, 4, 2, 1]
bookeeper = runningtotals()
print bookeeper.next()
for num in nums:
print num, bookeeper.send(num)
Выход будет выглядеть так:
RunningTotal(n=0, total=0)
9 RunningTotal(n=1, total=9)
8 RunningTotal(n=2, total=17)
None RunningTotal(n=2, total=17)
3 RunningTotal(n=3, total=20)
4 RunningTotal(n=4, total=24)
2 RunningTotal(n=5, total=26)
1 RunningTotal(n=6, total=27)
Метод send
реализует сопрограммы .
Если вы не сталкивались с Coroutines, они сложны, чтобы обернуть голову, потому что они меняют способ передачи программы. Вы можете прочитать хороший учебник для более подробной информации.
Меня тоже смутили. Вот пример, который я сделал при попытке настроить генератор, который дает и принимает сигналы в чередующемся порядке (выход, прием, выход, принятие) ...
def echo_sound():
thing_to_say = '<Sound of wind on cliffs>'
while True:
thing_to_say = (yield thing_to_say)
thing_to_say = '...'.join([thing_to_say]+[thing_to_say[-6:]]*2)
yield None # This is the return value of send.
gen = echo_sound()
print 'You are lost in the wilderness, calling for help.'
print '------'
in_message = gen.next()
print 'You hear: "{}"'.format(in_message)
out_message = 'Hello!'
print 'You yell "{}"'.format(out_message)
gen.send(out_message)
print '------'
in_message = gen.next()
print 'You hear: "{}"'.format(in_message)
out_message = 'Is anybody out there?'
print 'You yell "{}"'.format(out_message)
gen.send(out_message)
print '------'
in_message = gen.next()
print 'You hear: "{}"'.format(in_message)
out_message = 'Help!'
print 'You yell "{}"'.format(out_message)
gen.send(out_message)
Выход:
You are lost in the wilderness, calling for help.
------
You hear: "<Sound of wind on cliffs>"
You yell "Hello!"
------
You hear: "Hello!...Hello!...Hello!"
You yell "Is anybody out there?"
------
You hear: "Is anybody out there?...there?...there?"
You yell "Help!"
Это может помочь кому-то. Вот генератор, на который не влияет функция отправки. Он принимает параметр числа при создании экземпляра и не подвержен влиянию send:
>>> def double_number(number):
... while True:
... number *=2
... yield number
...
>>> c = double_number(4)
>>> c.send(None)
8
>>> c.next()
16
>>> c.next()
32
>>> c.send(8)
64
>>> c.send(8)
128
>>> c.send(8)
256
Теперь вот как вы будете делать один и тот же тип функции с помощью send, поэтому на каждой итерации вы можете изменить значение числа :
def double_number(number):
while True:
number *= 2
number = yield number
Вот как это выглядит, так как вы можете видеть, что посылка нового значения для числа меняет результат:
>>> def double_number(number):
... while True:
... number *= 2
... number = yield number
...
>>> c = double_number(4)
>>>
>>> c.send(None)
8
>>> c.send(5) #10
10
>>> c.send(1500) #3000
3000
>>> c.send(3) #6
6
Вы также можете поместить это в петля как таковая:
for x in range(10):
n = c.send(n)
print n
Для получения дополнительной справки ознакомьтесь с этим большим учебником .
Метод send()
управляет тем, что будет иметь значение слева от выражения yield.
Чтобы понять, как отличается выход и какое значение оно имеет, давайте сначала быстро обновить код заказа python оценивается.
Python оценивает выражения слева направо. Обратите внимание, что при оценке присваивания правая часть оценивается перед левой стороной.
blockquote>Итак, сначала оценивается правая часть
a = b
.Как показано ниже,
a[p('left')] = p('right')
сначала оценивается правая сторона.>>> def p(side): ... print(side) ... return 0 ... >>> a[p('left')] = p('right') right left >>> >>> >>> [p('left'), p('right')] left right [0, 0]
Что дает выход ?, выход, приостановка выполнения функции и возврат к вызывающей стороне, а также возобновление исполнение в том же месте, которое оно остановило до приостановки.
Где именно приостановлено исполнение? Возможно, вы уже догадались, что выполнение приостановлено между правой и левой стороной выражения yield. Таким образом,
new_val = yield old_val
выполнение останавливается на знаке=
, а значение справа (которое перед приостановкой, а также значение, возвращаемое вызывающему абоненту) может быть чем-то иным, чем значение слева (которое значение, назначенное после возобновления выполнения).
yield
дает 2 значения, один направо и другой влево.Как вы управляете значением в левой части выражения yield? с помощью метода
.send()
.Значение выражения yield после возобновления зависит от метода, который возобновил выполнение. Если используется
blockquote>__next__()
(как правило, с помощью встроенного в for илиnext()
), результатом является None. В противном случае, если используетсяsend()
, результатом будет значение, переданное этому методу.
Эта функция предназначена для записи сопрограмм
def coroutine():
for i in range(1, 10):
print("From generator {}".format((yield i)))
c = coroutine()
c.send(None)
try:
while True:
print("From user {}".format(c.send(1)))
except StopIteration: pass
prints
From generator 1
From user 2
From generator 1
From user 3
From generator 1
From user 4
...
Посмотрите, как управление передается взад и вперед? Это сопрограммы.
Подумайте об этом, используя генератор и не отправляйте, это улица с односторонним движением
========== yield ========
Generator | ------------> | User |
========== ========
] Но при отправке это становится двухсторонней улицей
========== yield ========
Generator | ------------> | User |
========== <------------ ========
send
, которая открывает дверь пользователю, настраивающему поведение генераторов на лету , и генератор, отвечающий пользователю .
send()
, генератор еще не достиг ключевого слова yield
.
– Michael
28 January 2017 в 04:07