карта с функцией для каждого элемента? - PullRequest
4 голосов
/ 06 апреля 2020

Очень часто я обрабатываю отдельные элементы кортежей, например:

size, duration, name = some_external_function()
size = int(size)
duration = float(duration)
name = name.strip().lower()

Если some_external_function вернет какой-то одинаково типизированный кортеж, я мог бы использовать map, чтобы иметь (более функциональное) закрытое выражение :

size, duration, name = map(magic, some_external_function())

Есть ли что-то вроде с точки зрения элемента map? Что-то, что я мог бы выполнить так:

size, duration, name = map2((int, float, strip), some_external_function())

Обновление : я знаю, что могу использовать понимание вместе с zip, например

size, duration, name = [f(v) for f, v in zip(
   (int, float, str.strip), some_external_function())]

- I ' ищу решение 'pythoni c' (лучшее: встроенное)!

Для Python разработчиков :

А как насчет

(size)int, (duration)float, (name)str.strip = some_external_function()

? Если я увижу это в какой-либо следующей версии Python, я пришлю вам пиво :) 1030 *

Ответы [ 6 ]

5 голосов
/ 06 апреля 2020

Все просто: используйте функцию и распаковываете аргументы ...

def transform(size, duration, name):
    return int(size), float(duration), name.strip().lower()

# if you don't know what the `*` does then follow the link above...    
size, name, duration = transform(*some_external_function())

Очень простой, отлично читаемый и тестируемый.

3 голосов
/ 06 апреля 2020
class SomeExternalData:
    def __init__(self, size: int, duration: float, name: str):
        self.size = size
        self.duration = duration
        self.name = name.strip().lower()

    @classmethod
    def from_strings(cls, size, duration, name):
        return cls(int(size), float(duration), name)


data = SomeExternalData.from_strings(*some_external_function())

Это далеко не однострочник, но это наиболее декларативный, читаемый, многократно используемый и поддерживаемый подход к решению этой проблемы IMO. Модель Ваши данные явно вместо обработки отдельных значений ad ho c.

3 голосов
/ 06 апреля 2020

Карта здесь не совсем применима. Это удобно, когда вы хотите применить простую функцию ко всем элементам списка, например map(float, list_ints).

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

funcs = int, float, lambda x: x.strip().lower()
t = 1., 2, 'Some String  ' # example returned tuple

size, duration, name = (f(i) for f,i in zip(funcs, t))

Или, возможно, немного чище:

def transform(t, funcs):
    return (f(i) for f,i in zip(funcs, t))

size, duration, name = transform(t, funcs)

size
# 1
duration
# 2.0
name
# 'some string'
2 голосов
/ 06 апреля 2020

AFAIK встроенного решения не существует, поэтому мы можем сами написать функцию generi c и использовать ее впоследствии

def map2(functions, arguments):  # or some other name
    return (function(argument) for function, argument in zip(functions, arguments))  # we can also return `tuple` here for example

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

size, duration, name = map2((int, float, str.strip), some_external_function())

Мы можем go далее с помощью functools.partial и дать имя нашему «трансформатору», например

from functools import partial
...
transform = partial(map2, (int, float, str.strip))

, и повторно использовать его в а также в других местах.

0 голосов
/ 13 апреля 2020

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

from functools import wraps

def type_wrangler(func):
    def wrangler():
        n,s,d = func()
        return str(n), int(s), float(d)
    return wrangler


def external_func():
    return 'a_name', '10', '5.6'

f = type_wrangler(external_func)

print(f())
0 голосов
/ 06 апреля 2020

Основываясь на преобразовании Бруно, которое, я думаю, является лучшим ответом на проблему, я хотел посмотреть, смогу ли я создать обобщенную функцию преобразования c, которая не нуждалась бы в жестко заданном наборе форматеров, но могла бы принимать любое количество элементов с соответствующим количеством форматеров.

(Это действительно излишне, если вам не нужно большое количество таких маги c картографов или если вам нужно генерировать их динамически.)

Здесь я использую гарантированный порядок словаря Python 3.6, чтобы "распаковать" форматеры в их объявленном порядке и отделить их от inputs variadi c.

def transform(*inputs, **tranformer):
    return [f(val) for val, f in zip(inputs, tranformer.values())]

size, duration, name = transform(*some_external_function(), f1=int, f2=float, f3=str.lower)

И сделать процесс ровным больше generi c и разрешить предопределенные функции преобразования, которые вы можете использовать operator.partial.

from functools import partial

def prep(f_tranformer, *format_funcs):
    formatters = {"f%d"%ix : func for ix, func in enumerate(format_funcs)} 
    return partial(transform, **formatters)


transform2 = prep(transform, int, float, str.lower)

, которые вы можете использовать как:

size, duration, name = transform2(*some_external_function())
...