如何组合两个函数,其外部函数为内部函数

时间:2016-12-28 20:41:19

标签: python functional-programming

我有两个类似的代码需要解析,我不确定实现这个目的的最pythonic方法。

假设我有两个相似的“代码”

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_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 example

9 个答案:

答案 0 :(得分:2)

repl.it example

功能方法更简洁,更有意义。

我们可以从pure functions中表达概念开始,这是最容易构成的形式。

剥离$$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)]

这两种类型的代码有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)

答案 1 :(得分:1)

我相信这样的事情可行:

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)

答案 2 :(得分:1)

我真的不确定你到底是什么意思。但我想到了你可能正在寻找的想法。

如何使用这样的简单函数:

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

而不仅仅是使用:

parse_code_1(*split_secret_code(secret_code_1))

答案 3 :(得分:1)

你嫁给了字符串解析吗?如果要使用值传递变量并且不需要变量名,则可以将它们“打包”为整数。

如果您正在使用加密技术,您可以制定一个长十六进制数字的字符,然后将其作为int传递“stop”字节(例如0000因为“0”实际上是48次尝试:chr(48))并且如果你嫁给了一个字符串我会建议一个较低的字符字节标识符,例如(1 - > aka try:chr(1))所以你可以扫描整数并将它移位8来获得8位掩码的字节(这看起来像(secret_code>>8)&0xf

哈希以类似的方式工作,因为可以解析一个带有 somename somevalue somename somevalue 的变量作为整数然后与stop模块连接,然后在需要时检索。

让我举一个散列的例子

# 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。

您可以在此处找到按位运算符binary operators

但请记住,65是数字,65是一个字符,它只关注那些发送的字节,如果它们被发送到显卡它们是像素,如果它们被发送到audiocard,它们是声音,如果它们被送到数学处理他们是数字,作为程序员,我们的操场。

但如果这不能解决您的问题,您可以随时使用地图。

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功能使其更清晰。

我希望这会有所帮助

答案 4 :(得分:1)

我不确定你正在使用什么约束,但它看起来像:

  1. 有不同类型的代码具有不同的规则
  2. 虚线分开的箭数可以变化
  3. 哪个arg有一个管道可以变化
  4. 直截了当的例子

    这不是很难解决,而且你不需要花哨的包装器,所以我会放弃它们,因为它增加了阅读的复杂性。

    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)
    

    更可扩展的示例

    如果由于某种原因你需要支持这种解析的许多变体,你可以使用简单的OOP设置,比如

    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)
    

答案 5 :(得分:1)

我的建议尝试以下内容:

  • 非常详细
  • 以明确的方式分离共同和特定的逻辑
  • 足够可扩展

基本上,它将公共逻辑和特定逻辑分成不同的函数(您可以使用OOP执行相同操作)。问题在于它使用一个映射器变量,该变量包含根据每个代码的内容选择特定解析器的逻辑。在这里:

    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]

答案 6 :(得分:0)

我认为您需要提供有关您正在尝试实现的内容的更多信息,以及明确的约束条件。例如,$$可以出现多少次?会不会有| dividor?那种事。

为了广泛地回答你的问题,一个优雅的pythonic方法是使用python的解包功能,结合split。例如

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

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

通过使用这种技术,除了简单的if块之外,您还应该能够编写一个优雅的解析器。

答案 7 :(得分:0)

如果我理解正确,你希望能够定义你的函数,就像传递解析的参数一样,但是想要将未解析的代码传递给函数。

您可以与您提供的第一个解决方案非常相似。

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

答案 8 :(得分:0)

我对你的问题一无所知,也不了解你的代码,但也许一个简单的方法是通过正则表达式来理解它?

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