Написание обобщенной функции для строк и списков в Python - PullRequest
7 голосов
/ 19 января 2012

Так что я зеленый, как трава, и учусь программированию у Как мыслить как ученый: изучай python 3 . Я могу ответить на вопрос (см. Ниже), но боюсь, что пропускаю урок.

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

test(insert_at_end(5, [1, 3, 4, 6]), **[1, 3, 4, 6, 5]**)
test(insert_at_end('x', 'abc'),  **'abcx'**)
test(insert_at_end(5, (1, 3, 4, 6)), **(1, 3, 4, 6, 5)**)

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

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

def encapsulate(val, seq):
    if type(seq) == type(""):
        return str(val)
    if type(seq) == type([]):
        return [val]
    return (val,)

def insert_at_end(val, seq): 
    return seq + encapsulate(val, seq)

Что, похоже, решает вопрос, проводя различие между списками и строками ... что противоречит подсказке. Так как насчет этого? Есть ли способ ответить на вопрос (и еще около 10 похожих), не различая? т.е. не использовать "type ()"

Ответы [ 9 ]

2 голосов
/ 19 января 2012

Мои лучшие усилия:

def insert_at_end(val, seq):
    t = type(seq)
    try:
        return seq + t(val)
    except TypeError:
        return seq + t([val])

Это попытается создать последовательность type(seq), а если val не повторяется, создаст список и объединит.

2 голосов
/ 19 января 2012

Я бы сказал, что пример не симметричный, то есть он просит читателя обработать два разных случая:

  • int, список
  • str, str

По моему мнению, упражнение должно попросить реализовать это:

  • список, список: insert_at_end ([5], [1, 3, 4, 6])
  • str, str: insert_at_end ('x', 'abc')

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

1 голос
/ 20 января 2012

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

  • + объединяет последовательности, но только последовательности одного типа.
  • значения, переданные в качестве первого аргумента insert_at_end, являются «скалярными», поэтому вам необходимо преобразовать их в тип последовательности, который имеет второй аргумент.
  • для этого нельзя просто вызвать конструктор последовательности со скалярным аргументом и создать последовательность из одного элемента такого типа: tuple(1) не работает.
  • str работает иначе, чем другие типы последовательностей: tuple(["a"]) равно ("a",), list(["a"]) равно ["a"], но str(["a"])) равно "['a']", а не "a".

Это делает + бесполезным в этой ситуации, даже если вы можете легко создать последовательность заданного типа без instanceof, просто используя type().

Вы также не можете использовать назначение слайсов, поскольку только списки являются изменяемыми.

В этой ситуации решение @Hamish выглядит максимально чистым.

1 голос
/ 19 января 2012

Эта проблема входит в длинный список, и подсказка относится ко всем из них.Я думаю, что разумно, что, написав функцию encapsulate, которая может быть повторно использована для таких вещей, как insert_at_front, остальная часть реализации не зависит от типа.encapsulate может быть:

def encapsulate(val, seq):
    if isinstance(seq, basestring):
        return val
    return type(seq)([val])

, который обрабатывает более широкий диапазон типов с меньшим количеством кода.

0 голосов
/ 20 января 2012

Я согласен, что дело в том, является ли item итеративным или нет.

Так что мое решение будет таким:

def iterate(seq, item):
    for i in seq:
        yield i
    yield item

def insert_at_end(seq, item):
    if hasattr(item, '__iter__'):
        return seq + item
    else:
        return type(seq)(iterate(seq, item))

Пример:

>>> insert_at_end('abc', 'x')
'abcx'
>>> insert_at_end([1, 2, 4, 6], 5)
[1, 2, 4, 6, 5]
>>> insert_at_end((1, 2, 4, 6), 5)
(1, 2, 4, 6, 5)

Так как insert_at_end может обрабатывать многократно и не может, отлично работает даже с:

>>> insert_at_end('abc', 'xyz')
'abcxyz'
>>> insert_at_end([1, 2, 4, 6], [5, 7])
[1, 2, 4, 6, 5, 7]
>>> insert_at_end((1, 2, 4, 6), (5, 7))
(1, 2, 4, 6, 5, 7)
0 голосов
/ 20 января 2012

Может быть, это ближе к ответу:

def genappend(x, s):
    if isinstance(s, basestring):
        t = s[0:0].join
    else:
        t = type(s)
    lst = list(s)
    lst.append(x)
    return t(lst)

print genappend(5, [1,2,3,4])    
print genappend(5, (1,2,3,4))
print genappend('5', '1234')

Также могут быть полностью определенные пользователем типы последовательностей.Они также будут работать до тех пор, пока их можно конвертировать в список и из списка.Это также работает:

print genappend('5', set('1234'))
0 голосов
/ 19 января 2012

Это решение все еще требует некоторого отдельного кода для строк, в отличие от списков / кортежей, но оно более сжатое и не выполняет никакой проверки для определенных типов.

def insert_at_end(val, seq):
    try:
        return seq + val
    except TypeError:   # unsupported operand type(s) for +
        return seq + type(seq)([val])
0 голосов
/ 19 января 2012

Проблема с этим вопросом (в Python 2.7, я сейчас тестирую 3.2, чтобы проверить) состоит в том, что два из возможных типов ввода для seq являются неизменяемыми, и вы должны вернуть тот же тип, что и передано. Для строк это не проблема, потому что вы можете сделать это:

return seq + char

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

if hasattr(seq, 'append'): # List input.
  seq.append(char)
elif hasattr(seq, 'strip'): # String input.
  seq = seq + char
else: # Tuple
  seq = seq + (char,)

return seq

Это на самом деле мало чем отличается от фактической проверки типов, но позволяет избежать непосредственного использования функции type.

0 голосов
/ 19 января 2012

Хотя encapsulate использует тип, код непосредственно в insert_at_end этого не делает, и полагается на +, что означает связанные вещи для всех трех типов, и поэтому в этом смысле соответствует подсказке.

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