python: атрибуты объекта-генератора - PullRequest
6 голосов
/ 05 июня 2009

Можно ли создать атрибут на объекте генератора?

Вот очень простой пример:

def filter(x):
    for line in myContent:
        if line == x:
            yield x

Теперь скажите, что у меня много этих объектов генератора фильтров, плавающих вокруг ... возможно, некоторые из них являются анонимными ... Я хочу вернуться позже и опросить их для того, для чего они фильтруют. Есть ли способ, которым я могу: а) опросить объект-генератор на значение х или б) установить атрибут со значением х, которое я смогу потом опросить?

Спасибо

Ответы [ 7 ]

16 голосов
/ 05 июня 2009

Да.

class Filter( object ):
    def __init__( self, content ):
        self.content = content
    def __call__( self, someParam ):
        self.someParam = someParam
        for line in self.content:
            if line == someParam:
                yield line
7 голосов
/ 05 июня 2009

К сожалению, объекты генератора (результаты, возвращаемые при вызове функции генератора) не поддерживают добавление произвольных атрибутов. Вы можете обойти это в некоторой степени, используя внешний дикт, индексируемый объектами генератора, поскольку такие объекты можно использовать как ключи к диктовке. Итак, где бы вы хотели сделать, скажите:

a = filter(23)
b = filter(45)
...
a.foo = 67
...
x = random.choice([a,b])
if hasattr(x, 'foo'): munge(x.foo)

вместо этого вы можете:

foos = dict()
a = filter(23)
b = filter(45)
...
foos[a] = 67
...
x = random.choice([a,b])
if x in foos: munge(foos[x])

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

3 голосов
/ 05 июня 2009

Если вы хотите опросить их в целях отладки, то поможет следующая функция:

import inspect

def inspect_generator(g):
    sourcecode = open(g.gi_code.co_filename).readlines()
    gline = g.gi_code.co_firstlineno
    generator_code = inspect.getblock(sourcecode[gline-1:])

    output = "Generator %r from %r\n" % (g.gi_code.co_name, g.gi_code.co_filename)
    output += "".join("%4s: %s" % (idx+gline, line) for idx, line in enumerate(generator_code))

    output += "Local variables:\n"
    output += "".join("%s = %r\n" % (key,value) for key,value in g.gi_frame.f_locals.items())

    return output

print inspect_generator(filter(6))
"""Output:
Generator 'filter' from 'generator_introspection.py'
   1: def filter(x):
   2:     for line in myContent:
   3:         if line == x:
   4:             yield x
Local variables:
x = 6
"""

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

2 голосов
/ 05 июня 2009

Нет. Вы не можете устанавливать произвольные атрибуты для генераторов.

Как указывает С. Лотт, у вас может быть объект, который выглядит как генератор, а действует как генератор. И если это похоже на утку и ведет себя как утка, у вас есть само определение печатания утки, прямо здесь.

Однако он не будет поддерживать атрибуты генератора, такие как gi_frame, без соответствующих методов прокси.

0 голосов
/ 23 января 2015

Я понимаю, что это очень запоздалый ответ, но ...

Вместо сохранения и последующего чтения какого-либо дополнительного атрибута, ваш код может позже просто проверить переменную (и) генератора, используя:

filter.gi_frame.f_locals

Полагаю, на это намекнул Муравей Аасма.

0 голосов

Я только что написал декоратор, чтобы сделать это здесь: http://code.activestate.com/recipes/577057-generator-attributes/

0 голосов
/ 05 июня 2009

Если подумать о проблеме, то - это способ заставить генераторы переносить набор атрибутов. Это немного безумно - я настоятельно рекомендую вместо этого предложение Алекса Мартелли - но оно может быть полезно в некоторых ситуациях.

my_content = ['cat', 'dog days', 'catfish', 'dog', 'catalog']

def filter(x):
    _query = 'I\'m looking for %r' % x

    def _filter():
        query = yield None
        for line in my_content:
            while query:
                query = yield _query

            if line.startswith(x):
                query = yield line

        while query:
            query = yield _query

    _f = _filter()
    _f.next()
    return _f

for d in filter('dog'):
    print 'Found %s' % d

cats = filter('cat')
for c in cats:
    looking = cats.send(True)
    print 'Found %s (filter %r)' % (c, looking)

Если вы хотите спросить у генератора, для чего он фильтруется, просто наберите send со значением, которое оценивается как true. Конечно, этот код, вероятно, слишком умен в два раза. Используйте с осторожностью.

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