Как программировать функции с альтернативными сигнатурами возвращаемого значения в Python? (next () для альтернативных итераторов) - PullRequest
0 голосов
/ 05 июня 2009

например. чтобы они оба работали - возможно ли это?

(val,VAL2) = func(args) 
val = func(args)

Где val равно не кортеж

Например, я бы хотел, чтобы они работали для моего пользовательского объекта что-то

for item in something:
    do_item(item) #where again item - is not a tuple

for (item,key) in something:
    do_more(key,item)

Я подумал, что мне нужно реализовать функцию next () двумя различными способами ...

edit: , как следует из ответов ниже, на самом деле этого делать не следует.

Ответы [ 8 ]

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

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

for item in something:  # Default iterator: returns non-tuple objects
    do_something(item)

for (item,key) in something.iter_pairs(): # iter_pairs returns different iterator
    do_something_else(item, key)

например. посмотрите объект словаря, который использует этот шаблон. for key in mydict перебирает ключи словаря. for k,v in mydict.iteritems() перебирает пары (ключ, значение).

[Редактировать] На всякий случай, если кто-то захочет увидеть, что я имею в виду под «серьезно неприятной проверкой байт-кода», вот быстрая реализация:

import inspect, opcode

def num_expected_results():
    """Return the number of items the caller is expecting in a tuple.

    Returns None if a single value is expected, rather than a tuple.
    """
    f = inspect.currentframe(2)
    code = map(ord, f.f_code.co_code)
    pos = f.f_lasti
    if code[pos] == opcode.opmap['GET_ITER']: pos += 1 # Skip this and the FOR_ITER
    if code[pos] > opcode.EXTENDED_ARG: pos +=5
    elif code[pos] > opcode.HAVE_ARGUMENT: pos +=3
    else: pos += 1
    if code[pos] == opcode.opmap['UNPACK_SEQUENCE']:
        return code[pos+1] + (code[pos+2] << 8)
    return None

Можно использовать что-то вроде:

class MagicDict(dict):
    def __iter__(self):
        if num_expected_results() == 2:
            for k,v in self.iteritems():
                yield k,v
        else:
            for k in self.iterkeys(): 
                yield k

d=MagicDict(foo=1, bar=2)

print "Keys:"
for key in d:
    print "   ", key
print "Values"    
for k,v in d:
    print "   ",k,v

Отказ от ответственности: Это невероятно хакерская, безумно плохая практика, и заставит заставить других программистов выследить вас и убить вас, если они когда-нибудь увидят это в реальном коде. Работает только на cpython (если что). Никогда не используйте это в производственном коде (или в этом отношении, вероятно, любой код).

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

Вы пробовали это? Это работает.

def myfunction(data):
    datalen = len(data)
    result1 = data[:datalen/2]
    result2 = data[datalen/2:]
    return result1, result2


a, b = myfunction('stuff')
print a
print b

c = myfunction('other stuff')
print c

На самом деле не существует такой вещи, как «возврат подписи». Все функции возвращают один объект. Кажется, вы возвращаете более одного, но на самом деле вы заключаете их в объект-контейнерный кортеж.

2 голосов
/ 05 июня 2009
>>> def func(a,b):
      return (a,b)

>>> x = func(1,2)
>>> x
(1, 2)
>>> (y,z) = func(1,2)
>>> y
1
>>> z
2

Это действительно не отвечает на ваш вопрос. Реальный ответ заключается в том, что левая часть присваивания не влияет на возвращаемый тип функции и не может использоваться для различения функций с разными типами возвращаемых значений. Как отмечалось в других ответах, функция может возвращать разные типы из разных операторов возврата, но она не знает, что находится на другой стороне знака равенства.

В случае этой функции она возвращает кортеж. Если вы присваиваете его x, x имеет значение кортежа. (y, z) в левой части задания «распаковка кортежей». Кортеж, возвращаемый функцией func (), распаковывается в y и z.

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

Да, это выполнимо:

def a(b):
if b < 5:
    return ("o", "k")
else:
    return "ko"

и результат:

>>> b = a(4)
>>> b
('o', 'k')
>>> b = a(6)
>>> b
'ko'

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

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

Обновление:

Учитывая пример использования, я бы написал разные генераторы для обработки:

class Something(object): 
   def __init__(self): 
       self.d = {'a' : 1, 
               'b' : 2, 
               'c' : 3} 

   def items(self): 
       for i in self.d.values(): 
           yield i 

  def items_keys(self): 
      for k,i in self.d.items(): 
          yield i,k 

something = Something()

for item in something.items():
....:     print item
....: 
1
3
2

for item,key in something.items_keys():
....:     print key, " : ", item
....: 
a  :  1
b  :  2
c  :  3

Или

Вы можете вернуть кортеж:

In [1]: def func(n):
   ...:     return (n, n+1)
   ...: 

In [2]: a,b = func(1)

In [3]: a
Out[3]: 1

In [4]: b
Out[4]: 2

In [5]: x = func(1)

In [6]: x
Out[6]: (1, 2)
1 голос
/ 05 июня 2009

Да, оба будут работать. В первом примере val1 и val2 будут иметь два значения. Во втором примере val будет иметь кортеж. Вы можете попробовать это в вашем интерпретаторе Python:

>>> def foo():
...   return ( 1, 2 )
...
>>> x = foo()
>>> (y,z) = foo()
>>> x
(1, 2)
>>> y
1
>>> z
2
1 голос
/ 05 июня 2009

Возможно, только если вы счастливы, что val будет кортежем из 2 предметов (или если args не обязательно будет одинаковым в обоих случаях). Первый случай - это то, что произойдет, если функция завершится чем-то вроде return 23, 45. Вот пример последней идеи:

def weirdfunc(how_many_returns):
  assert 1 <= how_many_returns <= 4
  return 'fee fie foo fum'.split()[:how_many_returns]

var1, var2 = weirdfunc(2)  # var1 gets 'fee', var2 gets 'fie'

var, = weirdfunc(1)  # var gets 'fee'
0 голосов
/ 05 июня 2009

Это требует большой путаницы. Вместо этого вы можете следовать dict с отдельными ключами, значениями, элементами и т. Д. Или использовать соглашение о присвоении имен неиспользуемым переменным с одним подчеркиванием. Примеры:

for k in mydict.keys(): pass
for k, v in mydict.items(): pass

for a, b in myobj.foo(): pass
for a, _ in myobj.foo(): pass
for _, b in myobj.foo(): pass

for _, _, _, d in [("even", "multiple", "underscores", "works")]:
    print(d)

for item in something: # or something.keys(), etc.
    do_item(item)

for item, key in something.items():
    do_more(key, item)

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

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