Как составить две функции, внешняя функция которых предоставляет аргументы внутренней функции

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

Предположим, что у меня есть два похожих "кода"

secret_code_1 = 'asdf|qwer-sdfg-wert$$otherthing'
secret_code_2 = 'qwersdfg-qw|er$$otherthing'

оба кода заканчиваются на $$otherthing и содержат число значений, разделенных символом -

Сначала я подумал о том, чтобы использовать functools.wrap для разделения некоторой общей логики на логику, специфичную для каждого типа кода, примерно так:

from functools import wraps

def parse_secret(f):
  @wraps(f)
  def wrapper(code, *args):
    _code = code.split('$$')[0]
    return f(code, *_code.split('-'))
  return wrapper

@parse_secret
def parse_code_1b(code, a, b, c):
  a = a.split('|')[0]
  return (a,b,c)

@parse_secret
def parse_code_2b(code, a, b):
  b = b.split('|')[1]
  return (a,b)

Однако, делая это, это путает некоторые параметры, которые вы должны фактически передать функциям parse_code_* i.e.

parse_code_1b(secret_code_1)
parse_code_2b(secret_code_2)

Итак, чтобы упростить формальные параметры функции, я изменил логику на что-то вроде этого:

def _parse_secret(parse_func, code):
  _code = code.split('$$')[0]
  return parse_func(code, *_code.split('-'))

def _parse_code_1(code, a, b, c):
  """
  a, b, and c are descriptive parameters that explain
  the different components in the secret code

  returns a tuple of the decoded parts
  """
  a = a.split('|')[0]
  return (a,b,c)

def _parse_code_2(code, a, b):
  """
  a and b are descriptive parameters that explain
  the different components in the secret code

  returns a tuple of the decoded parts
  """
  b = b.split('|')[1]
  return (a,b)

def parse_code_1(code):
  return _parse_secret(_parse_code_1, code)

def parse_code_2(code):
  return _parse_secret(_parse_code_2, code)

Теперь вам легче рассуждать о том, что вы передаете этим функциям:

parse_code_1(secret_code_1)
parse_code_2(secret_code_2)

Однако этот код значительно более подробный.

Есть ли лучший способ сделать это? Означает ли объектно-ориентированный подход с классами?

пример repl.it

Ответ 1

пример repl.it

Функциональные подходы более кратки и имеют больше смысла.

Мы можем начать с выражения понятий в чистых функциях, форма, которую легче всего составить.

Разделите $$otherthing и разделите значения:

parse_secret = lambda code: code.split('$$')[0].split('-')

Возьмите одно из внутренних значений:

take = lambda value, index: value.split('|')[index]

Заменить одно из значений на его внутреннее значение:

parse_code = lambda values, p, q: \
  [take(v, q) if p == i else v for (i, v) in enumerate(values)]

Эти 2 типа кодов имеют 3 отличия:

  • Число значений
  • Позиция для анализа "внутренних" значений
  • Позиция "внутренних" значений для принятия

И мы можем составлять функции синтаксического анализа, описывая эти различия. Сплит-значения сохраняются в упаковке, поэтому их легче компоновать.

compose = lambda length, p, q: \
  lambda code: parse_code(parse_secret(code)[:length], p, q)

parse_code_1 = compose(3, 0, 0)
parse_code_2 = compose(2, 1, 1)

И используйте сложенные функции:

secret_code_1 = 'asdf|qwer-sdfg-wert$$otherthing'
secret_code_2 = 'qwersdfg-qw|er$$otherthing'
results = [parse_code_1(secret_code_1), parse_code_2(secret_code_2)]
print(results)

Ответ 2

Я считаю, что что-то вроде этого может работать:

secret_codes = ['asdf|qwer-sdfg-wert$$otherthing', 'qwersdfg-qw|er$$otherthing']


def parse_code(code):
    _code = code.split('$$')
    if '-' in _code[0]:
        return _parse_secrets(_code[1], *_code[0].split('-'))
    return _parse_secrets(_code[0], *_code[1].split('-'))


def _parse_secrets(code, a, b, c=None):
    """
    a, b, and c are descriptive parameters that explain
    the different components in the secret code

    returns a tuple of the decoded parts
    """
    if c is not None:
        return a.split('|')[0], b, c
    return a, b.split('|')[1]


for secret_code in secret_codes:
    print(parse_code(secret_code))

Вывод:

('asdf', 'sdfg', 'wert')
('qwersdfg', 'er')

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

def _parse_secrets(code, *data):
    """
    data is descriptive parameters that explain
    the different components in the secret code

    returns a tuple of the decoded parts
    """
    i = 0
    decoded_secrets = []
    for secret in data:
        if '|' in secret:
            decoded_secrets.append(secret.split('|')[i])
        else:
            decoded_secrets.append(secret)
        i += 1
    return tuple(decoded_secrets)

Ответ 3

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

Как использовать простую функцию:

def split_secret_code(code): 
    return [code] + code[:code.find("$$")].split("-")

И чем просто использовать:

parse_code_1(*split_secret_code(secret_code_1))

Ответ 4

Вы женаты на парсе? Если вы передаете переменные со значениями и не нуждаетесь в именах переменных, вы можете "упаковать" их в целое число.

Если вы работаете с криптографией, вы можете сформулировать длинное шестнадцатеричное число символов, а затем передать его как int с байтами "stop" (например, 0000, поскольку "0" на самом деле 48 попыток: chr(48)), и если вы женат на строке, я бы предложил более низкий идентификатор байтового символа, например (1 → aka try: chr(1)), чтобы вы могли сканировать целое число и битовое смещение на 8, чтобы получить байты с 8-битной маской (это выглядело бы как (secret_code>>8)&0xf.

Хеширование работает аналогично, поскольку одна переменная с somename и somevalue, somename и somevalue могут быть проанализированы как целое, а затем объединены с модулем остановки, а затем извлекаются, когда это необходимо.

Позвольте мне привести пример хэширования

# lets say 
a = 1
# of sort hashing would be 
hash = ord('a')+(0b00<<8)+(1<<16)
#where a hashed would be 65633 in integer value on 64 bit computer
# and then you just need to find a 0b00 aka separator

если вы хотите использовать только переменные (имена не имеют значения), тогда вам нужно только хэшировать значение переменной, поэтому размер анализируемого значения намного меньше (не называйте часть и нет необходимости в разделителе (0b00), и вы можете используйте умножитель для деления необходимых данных один раз (0b00) в два раза (0b00, 0b00 < 8) и т.д.

a = 1
hash = a<<8 #if you want to shift it 1 byte

Но если вы хотите скрыть его, и вам нужен пример криптографии, вы можете выполнить вышеуказанные методы, а затем скремблировать, сместить (a- > b) или просто преобразовать его в другой тип позже. Вам просто нужно выяснить порядок операций, которые вы делаете. Поскольку a-STOP-b-PASS-c не равен a-PASS-b-STOP-c.

Здесь можно найти побитовые операторы двоичные операторы

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

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

def mapProcces(proccesList,listToMap):

   currentProcces = proccesList.pop(0)
   listToMap = map( currentProcces, listToMap )

   if proccesList != []: 
       return mapProcces( proccesList, listToMap )
   else:
       return list( listToMap )

то вы можете его сопоставить:

mapProcces([str.lower,str.upper,str.title],"stackowerflow")

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

secret_code_1 = 'asdf|qwer-sdfg-wert$$otherthing'
separ = "|,-,$".split(",")
secret_code_1 = [x if x not in separ else " " for x in secret_code_1]# replaces separators with empty chars
secret_code_1 = "".join(secret_code_1) #coverts list to a string
secret_code_1 = secret_code_1.split(" ") #it splited them to list
secret_code_1 = filter(None,secret_code_1) # filter empty chars ''
first,second,third,fourth,other = secret_code_1

И там у вас это, ваш secret_code_1 разделен и назначен на определенное количество переменных. Конечно, "используется как декларация, вы можете использовать все, что хотите, вы можете заменить каждый разделитель на" someseparator ", если хотите, а затем разделить на" someseparator". Вы также можете использовать функцию str.replace, чтобы сделать ее более ясной.

Я надеюсь, что это поможет

Ответ 5

Я не уверен, с какими ограничениями вы работаете, но выглядит так:

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

Простой пример

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

def pre_parse(code):
    dash_code, otherthing = code.split('$$')
    return dash_code.split('-')

def parse_type_1(code):
    dash_args = pre_parse(code)
    dash_args[0], toss = dash_args[0].split('|')
    return dash_args

def parse_type_2(code):
    dash_args = pre_parse(code)
    toss, dash_args[1] = dash_args[1].split('|')
    return dash_args

# Example call
parse_type_1(secret_code_1)

Попытка ответить на вопрос, как указано

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

def dash_args(code):
    dash_code, otherthing = code.split('$$')
    return dash_code.split('-')

def pre_parse(f):
    def wrapper(code):
        # HERE is where the outer function, the wrapper,
        # supplies arguments to the inner function.
        return f(code, *dash_args(code))
    return wrapper

@pre_parse
def parse_type_1(code, *args):
    new_args = list(args)
    new_args[0], toss = args[0].split('|')
    return new_args

@pre_parse
def parse_type_2(code, *args):
    new_args = list(args)
    toss, new_args[1] = args[1].split('|')
    return new_args

# Example call:
parse_type_1(secret_code_1)

Более расширяемый пример

Если по какой-то причине вам необходимо поддерживать множество вариантов такого разбора, вы можете использовать простую настройку ООП, например

class BaseParser(object):
    def get_dash_args(self, code):
        dash_code, otherthing = code.split('$$')
        return dash_code.split('-')

class PipeParser(BaseParser):
    def __init__(self, arg_index, split_index):
        self.arg_index = arg_index
        self.split_index = split_index

    def parse(self, code):
        args = self.get_dash_args(code)
        pipe_arg = args[self.arg_index]
        args[self.arg_index] = pipe_arg.split('|')[self.split_index]
        return args

# Example call
pipe_parser_1 = PipeParser(0, 0)
pipe_parser_1.parse(secret_code_1)
pipe_parser_2 = PipeParser(1, 1)
pipe_parser_2.parse(secret_code_2)

Ответ 6

Мое предложение пытается сделать следующее:

  • недостаточно многословно
  • для четкого разделения общей и конкретной логики
  • достаточно растяжимым

В принципе, он разделяет общую и конкретную логику на разные функции (вы можете сделать то же самое с помощью ООП). Дело в том, что он использует переменную mapper, которая содержит логику для выбора определенного парсера, в соответствии с каждым содержимым кода. Вот оно:

    def parse_common(code):
        """
        Provides common parsing logic.
        """
        encoded_components = code.split('$$')[0].split('-')
        return encoded_components

    def parse_code_1(code, components):
        """
        Specific parsing for type-1 codes.
        """
        components[0] = components[0].split('|')[0] # decoding some type-1 component
        return tuple([c for c in components])

    def parse_code_2(code, components):
        """
        Specific parsing for type-2 codes.
        """
        components[1] = components[1].split('|')[1] # decoding some type-2 component
        return tuple([c for c in components])

    def parse_code_3(code, components):
        """
        Specific parsing for type-3 codes.
        """
        components[2] = components[2].split('||')[0] # decoding some type-3 component
        return tuple([c for c in components])

    # ... and so on, if more codes need to be added ...

    # Maps specific parser, according to the number of components
    CODE_PARSER_SELECTOR = [
        (3, parse_code_1),
        (2, parse_code_2),
        (4, parse_code_3)
    ]

    def parse_code(code):
        # executes common parsing
        components = parse_common(code)

        # selects specific parser
        parser_info = [s for s in CODE_PARSER_SELECTOR if len(components) == s[0]]

        if parser_info is not None and len(parser_info) > 0:
            parse_func = parser_info[0][1]
            return parse_func(code, components)
        else:
            raise RuntimeError('No parser found for code: %s' % code)

    secret_codes = [
        'asdf|qwer-sdfg-wert$$otherthing',              # type 1 
        'qwersdfg-qw|er$$otherthing',                   # type 2
        'qwersdfg-hjkl-yui||poiuy-rtyu$$otherthing'     # type 3
        ]

    print [parse_code(c) for c in secret_codes]

Ответ 7

Я думаю, вам нужно предоставить больше информации о том, чего именно вы пытаетесь достичь, и о том, какие явные ограничения. Например, сколько раз может произойти $$? Всегда будет делитель |? Такого рода вещи.

Чтобы ответить на ваш вопрос в широком смысле, элегантный питонический способ сделать это - использовать функцию распаковки python в сочетании с split. например

secret_code_1 = 'asdf|qwer-sdfg-wert$$otherthing'

first_$$_part, last_$$_part = secret_code_1.split('$$')

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

Ответ 8

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

Вы можете сделать это очень похоже на первое предложенное вами решение.

from functools import wraps

def parse_secret(f):
  @wraps(f)
  def wrapper(code):
    args = code.split('$$')[0].split('-')
    return f(*args)
  return wrapper

@parse_secret
def parse_code_1(a, b, c):
  a = a.split('|')[0]
  return (a,b,c)

@parse_secret
def parse_code_2(a, b):
  b = b.split('|')[1]
  return (a,b)

Для секретных кодов, упомянутых в примерах,

secret_code_1 = 'asdf|qwer-sdfg-wert$$otherthing'

print (parse_code_1(secret_code_1))
>> ('asdf', 'sdfg', 'wert')

secret_code_2 = 'qwersdfg-qw|er$$otherthing'
print (parse_code_2(secret_code_2))
>> ('qwersdfg', 'er')

Ответ 9

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

import re

secret_code_1 = 'asdf|qwer-sdfg-wert$$otherthing'
secret_code_2 = 'qwersdfg-qw|er$$otherthing'

def parse_code(code):
    regex = re.search('([\w-]+)\|([\w-]+)\$\$([\w]+)', code)  # regular expression
    return regex.group(3), regex.group(1).split("-"), regex.group(2).split("-")

otherthing, first_group, second_group = parse_code(secret_code_2)

print(otherthing)  # otherthing, string
print(first_group)  # first group, list
print(second_group)  # second group, list

Выход:

otherthing
['qwersdfg', 'qw']
['er']