python中的关键字参数别名

时间:2017-01-21 20:45:34

标签: python parameters keyword-argument

对我来说,似乎很奇怪,有关键字参数(或参数)可以传递给函数或类的__init__方法。如何防止对您的代码不熟悉的用户犯错误?如何让用户立即(几乎本能地)熟悉您的代码,而无需编写错误或冗长的文档或许多试验和错误,以防止用户快速,轻松地使用您的代码或模块?

在python中我们很幸运,因为我们有helpdir函数,它们通常可以指导我们更好地理解一些函数参数。但有时写的__doc__字符串写得不好,什么也没解释。

让我举几个例子来说明我的意思:

    >>> help(str.lower)
    Help on method_descriptor:

        lower(...)
        S.lower() -> string

    Return a copy of the string S converted to lowercase

。 例如,这里我们有一些具有 ... 输入参数的函数。这个参数代表什么,对于完整的新手(就像我第一次潜入python时那样)这令人困惑,我常常跳过这一部分。

一些提供建议或教程的网站只是打印输出帮助功能文件或只是实现sed功能的许多功能之一。

1 functionality of example function

或直接来自python.org

str.lower()
返回字符串的副本,并将所有套接字符[4]转换为小写。 对于8位字符串,此方法取决于语言环境。

现在,对于那些刚开始编程并且不能(或者不能)潜入位和字节,地址等的人来说,这是一个只有主术士才能执行的古老咒语,不要甚至让我开始说明为什么这对非英语国家的人没有帮助。

对于这个特定的示例函数,我可以找出2-3个可以以不同方式完成其工作的其他示例,我还必须发现可以通过将字符串插入str.lower(here)部分来使用此示例函数

这里的一个大问题(我当然看到它)是一个带有少量谷歌搜索的str本身可以描述,它的功能只能通过维基百科来逻辑地结束。

如果我总结一下问题很简单,有没有办法在用作参数时修改关键字以接受比我们定义更多的名字,这样用户就不必在介绍教程的第一步中撕掉他/她的头发?

现在我知道你们中的一些人会说些什么:"如果你不理解它,就不要这样做"或者"我不是妈妈教你的东西" ...我必须说"分享是关心"和#34;你有时也需要帮助编码,否则你不会在这个网站上#34;。

2 个答案:

答案 0 :(得分:2)

我将此装饰器命名为kwargs。像魅力一样工作

def alias_param(param_name: str, param_alias: str) -> Callable:
    """
    Decorator for aliasing a param in a function

    Args:
        param_name: name of param in function to alias
        param_alias: alias that can be used for this param
    Returns:
    """
    def decorator(func: Callable):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            alias_param_value = kwargs.get(param_alias)
            if alias_param_value:
                kwargs[param_name] = alias_param_value
                del kwargs[param_alias]
            result = func(*args, **kwargs)
            return result
        return wrapper

然后可以像这样使用

@alias_param("param_1", alias='p1')
def function(param_1=None):
    return param_1

function(p1='value')

答案 1 :(得分:1)

这是我的解决方案,如果有人有更好的请评论或纠正。

这个想法:
这个想法是每个类都可以有一个特定属性的别名列表,因此用户可以(基于类名逻辑:>点需要x,y,z,名称属性,狗需要品种,名称,年龄,性别属性等等) on)基于其自己的内部逻辑来调用属性,而无需确切知道sed属性具有什么属性名称。

逻辑:
如果 function class 输入了一些关键字参数,那么我需要与sed参数关联的常用单词的最小列表。同义词和成语可以轻松搜索,但我会建议反对同义词的大名单,保持小2-3 +属性名称。然后我们需要的是这些别名映射到原始属性,因为我们作为代码需要知道如何调用属性而不调用getattr(self,someattributestring)

<强>码
按时间顺序,我们必须首先定义一个函数来生成别名。

# generate aliases for attributes
def generateAliases(*argListNames):

    returningValues = [] # this could be omitted if user wants to make generator
    la = returningValues.append # this could be omitted also 

    #dominated argListNames
    argListNames = map( str, argListNames ) #for simplicity convert to strings
    argListNames = map( str.lower , argListNames ) #for simplicity convert to lower string
    argListNames = list(argListNames) # back to list

    # small nameless lambda functions
    getFirstChr = lambda element: element[0] # getting first character
    conectedJoing= lambda connector,item,args: connecter.join([ item, args if not __isTL__(args) else connecter.join(args) ]) # generating joined string

    # list of string convertors used to generate aliases
    convertorList= [ lambda x: x , getFirstChr , str.title , str.upper , lambda x: getFirstChr( str.upper(x) ) ]

    for item in argListNames:
        ## since we dont want alias to repeat itself
        listNoitem = filter( lambda x: x!=item , argListNames )
        listNoitem = list(listNoitem)

        la(item) # if returningValues omitted use yield statement

        for conversion in convertorList: ##1 keeping up with for loops
            converted = conversion(item)

            for connecter in "_,".split(","):

                for listItem in listNoitem:

                    for cnvrt in convertorList: ##2 cnvrt is converted second stage : used to convert the whole list of items without current alias
                        cList = cnvrt(listItem)

                        la( conectedJoing(connecter,converted,cList) )# if returningValues omitted use yield statement


                la( conectedJoing(connecter,converted,listNoitem) )# if returningValues omitted use yield statement

    # if user wanted to make generator omit next lines
    returningValues = [ x.replace("_","") if x.endswith("_") else x for x in returningValues ]
    returningValues = sorted(set(returningValues)) 
    return list( map(str,returningValues) )

现在我们需要在函数中映射和检查这些参数,所以我们需要一些参数解析器。

## **kwargs argument parser , no error
def argumentParser(AprovedSequence,**kwargs):

    # AprovedSequence is suposed to be dictionary data type with {"original argument": generateAliases(originalArgumentName,somealias,somealias,...)

    """
        phrases the keyword arguments, 
            for example :     argumentParser(AprovedSequence,someArgument=somevalue,otherArgument=othervalue ... )
        then it checks if someArgument is needed by checking in AprovedSequence if name "someArgument" is found in sequence: 
        If "someArgument" is found in AprovedSequence it stores returns dictionary of DefaultKeys : Values 
            for example: DefaultKey for someArgument: somevalue

        input: 
            argumentParser(dict: AprovedSequence, kwargs)
        returns:
            dictionary of found attributes and their values

        !!important!! kwargs are not case sensitive in this case , so go crazy as long as you get the apropriate keyword!!
        if you dont know what kind of keywords are needed for class
            just type className.errorAttributeNames()
            for example point.errorAttributeNames()

    """
    if isinstance(AprovedSequence,dict):

        di = dict.items # dictionary.values(someDict)
        dk = dict.keys  # dictionary.keys(someDict)

        # managing the kwargs and aprooved sequence data
        toLowerStr = lambda el: str(el).lower() # conversion to lower string
        asingKey = lambda el: [ key for key in dk(AprovedSequence) if toLowerStr(el) in AprovedSequence[key] ][0] # asigning key 

        return { asingKey(k):v for k,v in di(kwargs) } # dictionary comprehension
    else: 
        raise TypeError("argumentPhraser function accepts only dictionary for a AprovedSequence aka first item")
        return None

<强>实施

def somefunction(**kwargs):
    aliases = {
        "val1":generateAliases("first","1"),
        "val2":generateAliases("second","2")
    }
    aproved = argumentParser(aliases,**kwargs)

    if "val1" in aproved.keys(): val1 = aproved["val1"]
    else: val1 = 0 # seting default value for val1

    if "val2" in aproved.keys(): val2 = aproved["val2"]
    else: val2 = 1 # seting default value for val2

    #do something or your code here

    return val1,val2

# for testing purposes 
for x in [ {"first":1} , {"second":2,"first":3} , {"f1":4,"s2":5} , {"f_1":6,"2_s":7} ]:
    # displaying imputed variables 
    form = ["passed "]
    form += [ "{} as {} ".format(key,value) for key,value in x.items() ]
    # implementing somefunciton
    print( "".join(form), somefunction(**x) )

输出

python27 -m kwtest
Process started >>>
passed first as 1  (1, 1)
passed second as 2 first as 3  (3, 2)
passed f1 as 4 s2 as 5  (4, 5)
passed 2_s as 7 f_1 as 6  (6, 7)
<<< Process finished. (Exit code 0)

python35 -m kwtest
Process started >>>
passed first as 1  (1, 1)
passed first as 3 second as 2  (3, 2)
passed f1 as 4 s2 as 5  (4, 5)
passed f_1 as 6 2_s as 7  (6, 7)
<<< Process finished. (Exit code 0)

如果在类中实现,则__init__中的流程类似,但__getitem____setitem____delitem__必须进行编码,以便他们也可以在别名中搜索属性名称。此外,可以使用self.attributes = list( aliases.keys())或类似名称生成属性名称。默认值可以存储在具有__kwdefaults__或&#39; 默认值&#39;的类中。取决于您的功能使用的数据类型。

此代码已在py2.7和py3.5上进行了测试,如您所见。

如果需要进一步说明
您可以在类全局属性或__init__内定义别名 进一步解释__getitem__

def __getitem__(self,item):
    if item in self.aliases.keys():
         return getattr(self,item)
    if any( item in value for value in self.aliases.values() ):
         item = [ key for key in self.aliases.keys() if item in self.aliases[key] ] [0]
         return getattr(self,item)
    if item in range( len( self.aliases.keys() ) ): 
         item = list( self.aliases.keys() )[item]
         return getattr(self,item)

进一步解释__setitem__

def __setitem__(self,item,value):
    item = self.__getitem__(self,item)
    #? must have `__dict__` method or class needs to be instanced from object like class someclass(object) 
    item = [ key for key in vars(self).items() if self[key] == item] [0]
    if item != None:
        setattr(self,item,value)