Что такое синтаксис python, когда переменная находится после «yield»? [Дубликат]

Исключение нулевого указателя генерируется, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  1. Вызов метода экземпляра объекта null.
  2. Доступ или изменение поля объекта null.
  3. Принимая длину null, как если бы это был массив.
  4. Доступ или изменение слотов null, как если бы это был массив.
  5. Бросок null как будто это было значение Throwable.

Приложения должны бросать экземпляры этого класса, чтобы указать на другие незаконные использования объекта null.

Ссылка: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

89
задан Kevin 10 October 2013 в 18:42
поделиться

7 ответов

Используется для отправки значений в генератор, который только что дал. Вот пример (не полезный):

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

Это намного сложнее и громоздко.

82
ответ дан evertheylen 18 August 2018 в 14:49
поделиться
  • 1
    Можете ли вы объяснить, какова цель этого? Почему это невозможно воссоздать с помощью double_inputs (startnumber) и yield? – Tommy 10 October 2013 в 18:49
  • 2
    @Tommy: о, потому что ценности, которые вы получили, не имеют ничего общего с предыдущим. позвольте мне изменить пример – Claudiu 10 October 2013 в 18:51
  • 3
    почему вы использовали бы это тогда по простой функции, которая удваивает ее вход? – Tommy 10 October 2013 в 18:53
  • 4
    @ Томми: Ты бы этого не сделал. Первый пример - просто объяснить, что он делает. Второй пример - это действительно полезный вариант использования. – Claudiu 10 October 2013 в 18:59
  • 5
    @Tommy: Я бы сказал, если вы действительно хотите узнать эту презентацию и проработать все это. Короткий ответ будет недостаточным, потому что тогда вы просто скажете «Но не могу ли я просто сделать это так?» и т.п. – Claudiu 10 October 2013 в 19:45

Некоторые примеры использования генератора и 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:

Обратите внимание, что:

  • существует линейная последовательность ожидаемых шагов

    • g12]
    • этапы могут отличаться (некоторые из них удаляются, некоторые добавляются в банк)
    • нам удается делать все это с помощью функций / генераторов - нет необходимости использовать сложный класс или аналогичные строфы.

    Запуск итогов

    Мы можем использовать генератор для отслеживания выполнения общего количества присланных ему значений.

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

    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)
    
6
ответ дан Asish M. 18 August 2018 в 14:49
поделиться

Метод send реализует сопрограммы .

Если вы не сталкивались с Coroutines, они сложны, чтобы обернуть голову, потому что они меняют способ передачи программы. Вы можете прочитать хороший учебник для более подробной информации.

11
ответ дан Jochen Ritzel 18 August 2018 в 14:49
поделиться

Меня тоже смутили. Вот пример, который я сделал при попытке настроить генератор, который дает и принимает сигналы в чередующемся порядке (выход, прием, выход, принятие) ...

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!"
1
ответ дан Peter 18 August 2018 в 14:49
поделиться

Это может помочь кому-то. Вот генератор, на который не влияет функция отправки. Он принимает параметр числа при создании экземпляра и не подвержен влиянию 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

Для получения дополнительной справки ознакомьтесь с этим большим учебником .

17
ответ дан radtek 18 August 2018 в 14:49
поделиться
  • 1
    Это сравнение между функцией, которая не получает влияния send () с тем, что действительно помогает. Благодаря! – Manas Bajaj 10 August 2018 в 10:42

Метод send() управляет тем, что будет иметь значение слева от выражения yield.

Чтобы понять, как отличается выход и какое значение оно имеет, давайте сначала быстро обновить код заказа python оценивается.

Раздел 6.15 Порядок оценки

Python оценивает выражения слева направо. Обратите внимание, что при оценке присваивания правая часть оценивается перед левой стороной.

Итак, сначала оценивается правая часть 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().

6.2.9. Выходные выражения

Значение выражения yield после возобновления зависит от метода, который возобновил выполнение. Если используется __next__() (как правило, с помощью встроенного в for или next()), результатом является None. В противном случае, если используется send(), результатом будет значение, переданное этому методу.

0
ответ дан user2059857 18 August 2018 в 14:49
поделиться

Эта функция предназначена для записи сопрограмм

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

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

61
ответ дан vsminkov 18 August 2018 в 14:49
поделиться
  • 1
    но функция генератора может принимать параметры. Как «Отправлять»? идти дальше отправки параметра в генератор? – Tommy 10 October 2013 в 18:57
  • 2
    @Tommy Потому что вы не можете изменить параметры генератора по мере его запуска. Вы даете ему параметры, они выполняются, выполняются. С отправкой вы даете ему параметры, он работает немного, вы отправляете ему значение, и оно делает что-то другое, повторяйте – jozefg 10 October 2013 в 19:00
  • 3
    @Tommy Это перезапустит генератор, который заставит вас повторить много работы – jozefg 10 October 2013 в 19:06
  • 4
    Не могли бы вы объяснить цель отправки None перед всем? – Shubham Aggarwal 19 January 2016 в 10:48
  • 5
    @ShubhamAggarwal Это делается, чтобы «запустить» генератор. Это просто то, что нужно сделать. Это имеет смысл, когда вы думаете об этом с первого раза, когда вы вызываете send(), генератор еще не достиг ключевого слова yield. – Michael 28 January 2017 в 04:07
Другие вопросы по тегам:

Похожие вопросы: