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

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

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

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_* какие параметры вы должны фактически передать в функции parse_code_* т.е.

 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

пример repl.it

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

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

Strip $$otherthing и split values:

 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) 

Я считаю, что что-то подобное может работать:

 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) 

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

Как насчет простой функции:

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

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

 parse_code_1(*split_secret_code(secret_code_1)) 

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

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

Прямой пример

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

 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) 

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

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

В принципе, он разделяет общую и конкретную логику на разные функции (вы можете сделать то же самое с помощью ООП). Дело в том, что он использует переменную 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] 

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

Если вы работаете с криптографией, вы можете сформулировать длинное шестнадцатеричное число символов, а затем передать его как 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 чтобы сделать ее более понятной.

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

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

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

 secret_code_1 = 'asdf|qwer-sdfg-wert$$otherthing' first_$$_part, last_$$_part = secret_code_1.split('$$') 

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

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

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

 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') 

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

 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'] 
Interesting Posts