python函数内定义函数_定义python函数时,高级python会考虑这10个元素 - Go语言中文社区

python函数内定义函数_定义python函数时,高级python会考虑这10个元素


python函数内定义函数

重点 (Top highlight)

No matter what implementation mechanisms programming languages use, all of them have a reserved seat for functions. Functions are essential parts of any code project because they’re responsible for preparing and processing data and configuring user interface elements. Without exception, Python, while positioned as an object-oriented programming language, depends on functions to perform data-related operations. So, writing good functions is critical to building a resilient code base.

不管编程语言使用哪种实现机制,它们都为功能保留了席位。 功能是任何代码项目中必不可少的部分,因为它们负责准备和处理数据以及配置用户界面元素。 毫无疑问,Python被定位为面向对象的编程语言,它依赖于函数来执行与数据相关的操作。 因此,编写良好的功能对于构建弹性代码库至关重要。

It’s straightforward to define a few simple functions in a small project. With the growth of the project scope, the functions can get far more complicated and the need for more functions grows exponentially. Getting all the functions to work together without any confusion can be a headache, even to experienced programmers. Applying best practices to function declarations becomes more important as the scope of your project grows. In this article, I’d like to talk about best practices for declaring functions — knowledge I have accrued over years of coding.

在一个小项目中定义一些简单的函数很简单。 随着项目范围的扩大,功能可能变得更加复杂,对更多功能的需求也呈指数增长。 即使没有经验的程序员,要想将所有功能完美地结合在一起也是一件令人头疼的事情。 随着项目范围的扩大,将最佳实践应用于函数声明变得越来越重要。 在本文中,我想谈一谈声明函数的最佳实践,这是我经过多年编码积累的知识。

1.一般准则 (1. General Guidelines)

You may be familiar with these general guidelines, but I’d like to discuss them first because they’re high-level, good practices that many programmers don’t appreciate. When developers don’t follow these guidelines, they pay the price — the code is very hard to maintain.

您可能熟悉这些通用准则,但我想首先讨论它们,因为它们是许多程序员不赞赏的高级高级实践。 当开发人员不遵循这些准则时,他们会付出代价-代码很难维护。

显式和有意义的名称 (Explicit and meaningful names)

We have to give meaningful names to our functions. As you know, functions are also objects in Python, so when we define a function, we basically create a variable of the function type. So, the variable name (i.e. the name of the function) has to reflect the operation it performs.

我们必须给我们的功能起有意义的名字。 如您所知,函数也是Python中的对象,因此在定义函数时,我们基本上会创建函数类型的变量。 因此,变量名称(即函数的名称)必须反映其执行的操作。

Although readability has become more emphasized in modern coding, it’s mostly talked about in regards to comments — it’s much less often discussed in relation to code itself. So, if you have to write extensive comments to explain your functions, it’s very likely that your functions don’t have good names. Don’t worry about having a long function name — almost all modern IDEs have excellent auto-completion hints, which will save you from typing the entire long names.

尽管在现代编码中可读性已得到越来越多的重视,但在注释方面却经常被谈论,而与代码本身有关的讨论则少得多。 因此,如果您必须写大量注释来解释您的功能,则您的功能很可能没有好名字。 不必担心函数名很长-几乎所有现代IDE都具有出色的自动完成提示,这将使您不必键入整个长名。

# Too generic, wanting others to guess what it does??
def foo(): pass


# Lack of details, requiring contexts to understand
def do_step1(): pass


# Not following naming conventions, which should be snake style and all lowercase
def GETData(): pass


# A few explicit and meaningful names
def get_account_info(): pass


def generate_sales_report(): pass

Good naming rules should also apply to the arguments of the function and all local variables within the function. Something else to note is that if your functions are intended to be used within your class or module, you may want to prefix the name with an underscore (e.g., def _internal_fun():) to indicate that these functions are for private usages and they’re not public APIs.

好的命名规则也应适用于函数的参数以及函数内的所有局部变量。 还有一点需要注意的是,如果打算在类或模块中使用您的函数,则可能要在名称前加上下划线(例如def _internal_fun():以表明这些函数供私人使用,它们不是公开的API。

小型单用途 (Small and Single Purpose)

Your functions should be kept small, so they’re easier to manage. Imagine that you’re building a house (not a mansion). However, the bricks you’re using are one meter cubed. Are they easy to use? Probably not — they’re too large. The same principle applies to functions. The functions are the bricks of your project. If the functions are all enormous in size, your construction won’t progress as smoothly as it could. When they’re small, they’re easier to fit into various places and moved around if the need arises.

您的功能应保持较小,以便于管理。 想象一下,您正在盖房子(不是豪宅)。 但是,您使用的砖是一米的立方。 它们易于使用吗? 可能不是-它们太大了。 相同的原理适用于功能。 功能是项目的基础。 如果功能全部庞大,那么您的构建将无法顺利进行。 当它们很小时,它们更容易放入各种地方并在需要时四处移动。

It’s also key for your functions to serve single purposes, which can help you keep your functions small. Another benefit of single-purpose functions is that you’ll find it much easier to name such functions. You can simply name your function based on its intended single purpose. The following is how we can refactor our functions to make each of them serve only one purpose each. Another thing to note is that by doing that, you can minimize the comments that you need to write — because all the function names tell the story.

这也是功能实现单一目的的关键,这可以帮助您使功能保持较小。 单一功能的另一个好处是,您会发现命名此类功能要容易得多。 您可以简单地根据其预期的单一用途来命名函数。 以下是我们如何重构我们的功能,以使每个功能仅用于一个目的。 要注意的另一件事是,这样做可以最大限度地减少需要编写的注释,因为所有函数名都可以说明问题。

# Embed all operations with one single function
def process_data():
    # a bunch of code to read data
    # a bunch of code to clean the data
    # a bunch of code to generate the report
    pass


# Refactor by create additional smaller functions
def read_data_from_path(filepath):
    return data


def clean_data(data): 
    return cleaned_data


def generate_report(cleaned_data):
    return report


def process_data():
    data = read_data_from_path(filepath="path_to_file.csv")
    cleaned_data = clean_data(data)
    report = generate_report(cleaned_data)
    return report

不要重新发明轮子 (Don’t reinvent the wheel)

You don’t have unlimited energy and time to write functions for every operation you need, so it’s essential to be familiar with common functions in standard libraries. Before you define your own functions, think about whether the particular business need is common — if so, it’s likely that these particular and related needs have already been addressed.

您没有无限的精力和时间来编写所需的每项操作的函数,因此熟悉标准库中的常见函数至关重要。 在定义自己的功能之前,请考虑一下特定的业务需求是否是通用的—如果是这样,则可能已经解决了这些特定的需求和相关需求。

For instance, if you work with data in the CSV format, you can look into the functionalities in the CSV module. Alternatively, the pandas library can handle CSV files gracefully. For another instance, if you want to count elements in a list, you should consider the Counter class in the collections module, which is designed specifically for these operations.

例如,如果您使用CSV格式的数据,则可以查看CSV模块中的功能。 另外, pandas库可以优雅地处理CSV文件。 对于另一个实例,如果要对列表中的元素进行计数,则应考虑collections模块中的Counter类,该类专门针对这些操作而设计。

2.默认参数 (2. Default Arguments)

相关场景 (Relevant scenarios)

When we first define a function, it usually serves one particular purpose. However, when you add more features to your project, you may realize that some closely related functions can be merged. The only difference is that the invocation of the merged function sometimes involves passing another argument or setting slightly different arguments. In this case, you can consider setting a default value to the argument.

当我们第一次定义一个函数时,它通常有一个特定的用途。 但是,当您向项目中添加更多功能时,您可能会意识到可以合并一些紧密相关的功能。 唯一的区别是合并函数的调用有时涉及传递另一个参数或设置略有不同的参数。 在这种情况下,您可以考虑为参数设置默认值。

The other common scenario is that when you declare a function, you already expect that your function serves multiple purposes, with function calls using differential parameters while some other parameters requiring few variations. You should consider setting a default value to the less varied argument.

另一个常见的情况是,在声明一个函数时,您已经期望该函数有多种用途,其中函数调用使用差分参数,而其他一些参数几乎不需要变化。 您应该考虑为较少变化的参数设置默认值。

设置默认参数 (Set default arguments)

The benefit of setting default arguments is straightforward — you don’t need to deal with setting unnecessary arguments in most cases. However, the availability of keeping these parameters in your function signature allows you to use your functions more flexibly when you need to. For instance, for the built-in sorted() function, there are several ways to call the function, but in most cases, we just use the basic form: sorted(the_iterable), which will sort the iterable in the ascending lexicographic order. However, when you want to change the ascending order or the default lexicographic order, we can override the default setting by specifying the reverse and key arguments.

设置默认参数的好处很简单-在大多数情况下,您无需处理设置不必要的参数。 但是,将这些参数保留在函数签名中的可用性使您可以在需要时更灵活地使用函数。 例如,对于内置的sorted()函数,有几种方法可以调用该函数,但在大多数情况下,我们仅使用基本形式: sorted(the_iterable) ,它将按字典顺序升序对iterable进行排序。 但是,当您想更改升序或默认字典顺序时,我们可以通过指定reversekey参数来覆盖默认设置。

We should apply the same practice to our own function declaration. In terms of what value we should set, the rule of thumb is you should choose the default value that is to be used for most function calls. Because this is an optional argument, you (or the users of your APIs) don’t want to set it in most situations. Consider the following example:

我们应该对我们自己的函数声明采用相同的做法。 根据我们应该设置的值,经验法则是您应该选择用于大多数函数调用的默认值。 因为这是一个可选参数,所以您(或您的API用户)在大多数情况下都不想设置它。 考虑以下示例:

# Set the price to the sale price or clearance sale with has addition discount
def set_regular_sale_price(price, discount):
    price *= discount
    return price




def set_clearance_sale_price(price, discount, additional_discount):
    sale_price = set_sale_price(price, discount)
    return sale_price * additional_discount




# A refactored combined function
def set_sale_price(price, discount, additional_discount=1):
    sale_price = price * discount
    return sale_price * additional_discount

避免易变的默认参数的陷阱 (Avoid the pitfalls of mutable default arguments)

There is a catch for setting the default argument. If your argument is a mutable object, it’s important that you don’t set it using the default constructor — because functions are objects in Python and they’re created when they’re defined. The side effect is that the default argument is evaluated at the time of function declaration, so a default mutable object is created and becomes part of the function. Whenever you call the function using the default object, you’re essentially accessing the same mutable object associated with the function, although your intention may be having the function to create a brand new object for you. The following code snippet shows you the unwanted side effect of setting a default mutable argument:

有一个设置默认参数的陷阱。 如果您的参数是一个可变对象,请务必不要使用默认构造函数对其进行设置,这很重要-因为函数是Python中的对象,并且它们是在定义时创建的。 副作用是,在函数声明时对默认参数进行了评估,因此将创建一个默认的可变对象,并成为该函数的一部分。 每当您使用默认对象调用该函数时,实际上您都在访问与该函数关联的相同可变对象,尽管您的意图可能是希望该函数为您创建一个全新的对象。 以下代码段向您展示了设置默认可变参数的有害副作用:

>>> def add_item_to_cart(new_item, shopper_name, existing_items=[]):
...     existing_items.append(new_item)
...     print(f"{shopper_name}'s cart has {existing_items}")
...     return existing_items
... 
... 
... shopping_list_wife = add_item_to_cart("Dress", "Jennifer")
... shopping_list_husband = add_item_to_cart("Soccer", "David")
... 
Jennifer's cart has ['Dress']
David's cart has ['Dress', 'Soccer']

As shown above, although we intended to create two distinct shopping lists, the second function call still accessed the same underlying object, which resulted in the Soccer item added to the same list object. To solve the problem, we should use the following implementation. Specifically, you should use None as the default value for a mutable argument:

如上所示,尽管我们打算创建两个不同的购物清单,但是第二个函数调用仍访问相同的基础对象,这导致将Soccer项目添加到同一清单对象中。 要解决该问题,我们应该使用以下实现。 具体来说,您应该使用None作为可变参数的默认值:

def add_item_to_cart(new_item, shopper_name, existing_items=None):
    if existing_items is None:
        existing_items = list()
    existing_items.append(new_item)
    print(f"{shopper_name}'s cart has {existing_items}")
    return existing_items

3.考虑返回多个值 (3. Consider Returning Multiple Values)

元组中的多个值 (Multiple values in a tuple)

When your function performs complicated operations, the chances are that these operations can generate two or more objects, all of which are needed for your subsequent data processing. Theoretically, it’s possible that you can create a class to wrap these objects such that your function can return the class instance as its output. However, it’s possible in Python that a function can return multiple values. More precisely speaking, these multiple values are returned as a tuple object. The following code shows you a trivial example:

当您的函数执行复杂的操作时,这些操作很可能会生成两个或多个对象,所有这些对于后续的数据处理都是必需的。 从理论上讲,可以创建一个类来包装这些对象,以便函数可以将类实例作为其输出返回。 但是,在Python中,函数可能会返回多个值。 更准确地说,这些多个值作为元组对象返回。 以下代码显示了一个简单的示例:

>>> from statistics import mean, stdev
... 
... def evaluate_test_result(scores):
...     scores_mean = mean(scores)
...     scores_std = stdev(scores)
...     return scores_mean, scores_std
... 
... evaluation_result = evaluate_test_result([1, 1, 1, 2, 2, 2, 6, 6, 6])
... print(f"Evaluation Result ({type(evaluation_result)}): {evaluation_result}")
... 
Evaluation Result (<class 'tuple'>): (3, 2.29128784747792)

As shown above, the returned values are simply separated by a comma, which essentially creates a tuple object, as checked by the type() function.

如上所示,返回值由逗号简单分隔,实际上由type()函数检查它会创建一个元组对象。

但不超过三个 (But no more than three)

One thing to note is that although Python functions can return multiple values, you should not abuse this feature. One value (when a function doesn’t explicitly return anything, it actually returns None implicitly) is best — because everything is straightforward and most users usually expect a function to return only one value. In some cases, returning two values is fine, returning three values is probably still OK, but please don’t ever return four values. It can create a lot of confusion for the users over which are which. If it happens, this is a good indication that you should refactor your functions — your functions probably serve multiple purposes and you should create smaller ones with more dedicated responsibilities.

需要注意的一件事是,尽管Python函数可以返回多个值,但您不应滥用此功能。 一个值(当一个函数不显式返回任何值时,它实际上隐式返回None )是最好的-因为一切都很简单,而且大多数用户通常希望函数仅返回一个值。 在某些情况下,返回两个值很好,返回三个值可能仍然可以,但是请不要返回四个值。 它会给哪个用户带来很多混乱。 如果发生这种情况,这很好地表明您应该重构您的功能-您的功能可能有多种用途,您应该创建较小的功能并承担更多责任。

4.使用Try…除外 (4. Use Try…Except)

When you define functions as public APIs, you can’t always assume that the users set the desired parameters to the functions. Even if we use the functions ourselves, it’s possible that some parameters are created out of our control and they’re incompatible with our functions. In these cases, what should we do in our function declaration?

在将函数定义为公共API时,不能总是假定用户为函数设置了所需的参数。 即使我们自己使用函数,也有可能某些参数是在我们的控制范围之外创建的,并且与我们的函数不兼容。 在这些情况下,我们应该在函数声明中做什么?

The first consideration is to use the try…except statement, which is the typical exception handling technique. You embed the code that can possibly go wrong (i.e., raise certain exceptions) in the try clause and the possible exceptions are handled in the except clause.

首先要考虑的是使用try…except语句,这是典型的异常处理技术。 您将可能会出错(即引发某些异常)的代码嵌入try子句中,并且可能的异常在except子句中进行处理。

Let’s consider the following scenario. Suppose that the particular business need is that your function takes a file path and if the file exists and is read successfully, your function does some data processing operations with the file and returns the result, otherwise returns -1. There are multiple ways to implement this need. The code below shows you a possible solution:

让我们考虑以下情形。 假设特定的业务需求是您的函数采用文件路径,并且如果文件存在并且被成功读取,则您的函数会对文件进行一些数据处理操作并返回结果,否则返回-1 。 有多种方法可以实现此需求。 下面的代码为您展示了一种可能的解决方案:

def get_data_from_file(filepath):
    try:
        with open(filepath) as file:
            computed_value = process_data(file)
    except Exception:
        return -1
    else:
        return computed_value


def process_data(file):
    # process the data
    return computed_value

In other words, if you expect that users of your functions can set some arguments that result in exceptions in your code, you can define functions that handle these possible exceptions. However, this should be communicated with the users clearly, unless it’s part of the feature as shown in the example (return -1 when the file can’t be read).

换句话说,如果您期望函数的用户可以设置一些导致代码中出现异常的参数,则可以定义处理这些可能异常的函数。 但是,应该清楚地与用户沟通,除非它是示例中所示的功能的一部分(当无法读取文件时返回-1 )。

5.考虑参数验证 (5. Consider Argument Validation)

The previous function using the try…except statement is sometimes referred to as the EAFP (Easier to Ask Forgiveness than Permission) coding style. There is another coding style called LBYL (Look Before You Leap), which stresses the sanity check before running particular code blocks.

以前使用try…except语句的函数有时被称为EAFP(比许可要求更容易获得宽恕)编码风格。 还有另一种称为LBYL的编码样式(请先了解一下),该样式强调在运行特定代码块之前进行的完整性检查。

Following the previous example, in terms of applying LBYL to function declaration, the other consideration is to validate your function’s arguments. One common use case for argument validation is to check whether the argument is of the right data type. As we all know, Python is a dynamically-typed language, which doesn’t enforce type checking. For instance, your function’s arguments should be integers or floating-point numbers. However, calling the function by setting strings — the invocation itself — won’t prompt any error messages until the function is executed.

在前面的示例之后,就将LBYL应用于函数声明而言,另一个考虑因素是验证函数的参数。 参数验证的一种常见用例是检查参数是否具有正确的数据类型。 众所周知,Python是一种动态类型的语言,它不执行类型检查。 例如,函数的参数应为整数或浮点数。 但是,通过设置字符串(调用本身)来调用函数,直到函数执行后才会提示任何错误消息。

The following code shows how to validate the arguments before running the code:

以下代码显示了在运行代码之前如何验证参数:

# Check type before running the code
def add_numbers(a, b):
    if not(isinstance(a, (float, int)) and isinstance(b, (float, int))):
        raise TypeError("Numbers are required.")
    return a + b

讨论:EAFP与LBYL (Discussion: EAFP vs. LBYL)

It should be noted that both EAFP and LBYL can be applied to more than just dealing with function arguments. They can be applied anywhere in your functions. Although EAFP is a preferred coding style in the Python world, depending on your use case, you should also consider using LBYL which can provide more user-friendly function-specific error messages than the generic built-in error messages you get with the EAFP style.

应该注意的是,EAFP和LBYL不仅可以用于处理函数参数,还可以应用于其他方面。 它们可以应用于您的函数中的任何位置。 尽管EAFP在Python世界中是首选的编码样式,但是根据您的用例,您还应该考虑使用LBYL,它可以提供比EAFP样式所提供的通用内置错误消息更多的用户友好的特定于函数的错误消息。 。

6.将Lambda函数视为替代方法 (6. Consider Lambda Functions As Alternatives)

作为其他功能的参数 (Functions as parameters of other functions)

Some functions can take another function (or are callable, in general terms) to perform particular operations. For instance, the sorted() function has the key argument that allows us to define more custom sorting behaviors. The following code snippet shows you a use case:

一些功能可以采用其他功能(或一般而言可调用)来执行特定操作。 例如, sorted()函数具有key参数,该参数允许我们定义更多自定义排序行为。 以下代码段显示了一个用例:

>>> # A list of dictionary objects for sorting
>>> grades = [{'name': 'John', 'score': 97},
...           {'name': 'David', 'score': 96},
...           {'name': 'Jennifer', 'score': 98},
...           {'name': 'Ashley', 'score': 94}]
>>> def sorting_grade(x):
...     return x['score']
... 
>>> sorted(grades, key=sorting_grade)
[{'name': 'Ashley', 'score': 94}, {'name': 'David', 'score': 96}, {'name': 'John', 'score': 97}, {'name': 'Jennifer', 'score': 98}]

Lambda可以替代 (Lambda functions as alternatives)

Notably, the sorting_grade function was used just once and it’s a simple function — in which case, we can consider using a lambda function.

值得注意的是, sorting_grade函数仅使用了一次,这是一个简单的函数-在这种情况下,我们可以考虑使用lambda函数。

If you’re not familiar with the lambda function, here’s a brief description. A lambda function is an anonymous function declared using the lambda keyword. It takes zero to more arguments and has one expression for applicable operations with the form: lambda arguments: expression. The following code shows you how we can use a lambda function in the sorted() function, which looks a little cleaner than the solution above:

如果您不熟悉lambda函数,这里有一个简短的描述。 lambda函数是使用lambda关键字声明的匿名函数。 它需要零到更多的参数,并且具有一个适用于操作的表达式,其形式为: lambda arguments: expression 。 以下代码向您展示了如何在sorted()函数中使用lambda函数,该函数看起来比上述解决方案还干净一些:

>>> sorted(grades, key=lambda x: x['score'])
[{'name': 'Ashley', 'score': 94}, {'name': 'David', 'score': 96}, {'name': 'John', 'score': 97}, {'name': 'Jennifer', 'score': 98}]

Another common use-case that’s relevant to many data scientists is the use of lambda functions when they work with the pandas library. The following code is a trivial example how a lambda function assists data manipulation using the map() function, which operates each item in a pandas Series object:

与许多数据科学家相关的另一个常见用例是在与熊猫库配合使用时使用lambda函数。 以下代码是一个简单的示例, lambda函数如何使用map()函数辅助数据处理,该函数操作pandas Series对象中的每个项目:

>>> import pandas as pd
>>> interest_rates = pd.Series([0.023, 0.025, 0.037])
>>> interest_rates.map(lambda x: f"{x:.2%}")
0    2.30%
1    2.50%
2    3.70%
dtype: object

7.考虑装饰器 (7. Consider Decorators)

装饰工 (Decorators)

Decorators are functions that modify the behavior of other functions without affecting their core functionalities. In other words, they provide modifications to the decorated functions at the cosmetic level. If you don’t know too much about decorators, please feel free to refer to my earlier articles (1, 2, and 3). Here’s a trivial example of how decorators work in Python.

装饰器是在不影响其核心功能的情况下修改其他功能的行为的功能。 换句话说,它们在装饰水平上对装饰功能进行了修改。 如果你不知道太多的装饰,请随时参考我以前的文章( 123 )。 这是装饰器如何在Python中工作的简单示例。

>>> # Define a decorator function
... def echo_wrapper(func):
...     def wrapper(*args, **kwargs):
...         func(*args, **kwargs)
...         func(*args, **kwargs)
...     return wrapper
... 
>>> # Define a function that is decorated by echo_wrapper
... @echo_wrapper
... def say_hello():
...     print('Hello!')
... 
>>> # Call the decorated function
... say_hello()
Hello!
Hello!

As shown, the decorator function simply runs the decorated function twice. To use the decorator, we simply place the decorator function name above the decorated function with an @ prefix. As you can tell, the decorated function did get called twice.

如图所示,装饰器函数只需将装饰函数运行两次。 要使用装饰器,我们只需将装饰器函数名称放在带有@前缀的装饰函数上方即可。 如您所知,修饰函数确实被调用了两次。

在函数声明中使用装饰器 (Use decorators in function declarations)

For instance, one useful decorator is the property decorator that you can use in your custom class. The following code shows you how it works. In essence, the @property decorator converts an instance method to make it behave like a regular attribute, which allows the access of using the dot notation.

例如,一种有用的装饰器是可以在自定义类中使用的属性装饰器。 以下代码向您展示了它是如何工作的。 本质上, @property装饰器将转换一个实例方法以使其表现得像常规属性,从而允许使用点表示法进行访问。

>>> class Product:
...     def __init__(self, item_id, price):
...         self.item_id = item_id
...         self.price = price
... 
...     @property
...     def employee_price(self):
...         return self.price * 0.9
... 
>>> product = Product(12345, 100)
>>> product.employee_price
90.0

Another trivial use case of decorators is the time logging decorator, which can be particularly handy when the efficiency of your functions is of concern. The following code shows you such a usage:

装饰器的另一个琐碎用例是时间记录装饰器,当您关注功能的效率时,它可能特别方便。 以下代码向您展示了这种用法:

>>> from time import time
... 
... # Create the decorator function
... def logging_time(func):
...     def logged(*args, **kwargs):
...         start_time = time()
...         func(*args, **kwargs)
...         elapsed_time = time() - start_time
...         print(f"{func.__name__} time elapsed: {elapsed_time:.5f}")
... 
...     return logged
... 
... @logging_time
... def calculate_integer_sum(n):
...     return sum(range(n))
... 
... @logging_time
... def calculate_integer_square_sum(n):
...     return sum(x*x for x in range(n))
... 
>>> calculate_integer_sum(10000)
calculate_integer_sum time elapsed: 0.00027
>>> calculate_integer_square_sum(10000)
calculate_integer_square_sum time elapsed: 0.00110

8.使用* args和** kwargs-但精简 (8. Use *args and **kwargs — But Parsimoniously)

In the previous section, you saw the use of *args and **kwargs in defining our decorator function, the use of which allows the decorator function to decorate any functions. In essence, we use *args to capture all (or an undetermined number of, to be more general) position arguments while **kwargs to capture all (or an undetermined number of, to be more general) keyword arguments. Specifically, position arguments are based on the positions of the arguments that are passed in the function call, while keyword arguments are based on setting parameters to specifically named function arguments.

在上一节中,您看到了在定义装饰器函数时使用了*args**kwargs ,使用它们可以使装饰器函数来装饰任何函数。 从本质上讲,我们使用*args捕获所有(或更不确定的数量,更通用)的位置参数,而**kwargs捕获所有(或更不确定的数量,更通用的)关键字参数。 具体而言,位置参数基于在函数调用中传递的参数的位置,而关键字参数基于将参数设置为专门命名的函数参数。

If you’re unfamiliar with these terminologies, here’s a quick peek to the signature of the built-in sorted() function: sorted(iterable, *, key=None, reverse=False). The iterable argument is a position argument, while the key and reverse arguments are keyword arguments.

如果您不熟悉这些术语,可以快速查看一下内置sorted()函数的签名: sorted( iterable , * , key=None , reverse=False )iterable参数是位置参数,而keyreverse参数是关键字参数。

The major benefit of using *args and **kwargs is to make your function declaration looks clean, or less noisy for the same matter. The following example shows you a legitimate use of *arg in function declaration, which allows your function to accept any number of position arguments.

使用*args**kwargs的主要好处是使您的函数声明看起来很整洁,或在同一情况下噪音较小。 以下示例向您展示了在函数声明中合法使用*arg情况,该声明允许您的函数接受任意数量的位置参数。

>>> # Define a function that accepts undetermined position arguments
>>> def stringify(*args):
...     return [str(x) for x in args]
... 
>>> stringify(2, False, None)
['2', 'False', 'None']

The following code shows you a legitimate use of **kwargs in function declaration. Similarly, the function with **kwargs allows the users to set any number of keyword arguments, to make your function more flexible.

以下代码显示了在函数声明中合法使用**kwargs情况。 同样,带有**kwargs的函数允许用户设置任意数量的关键字参数,以使您的函数更灵活。

>>> # Define a function that accepts undetermined keyword arguments
... def generate_score_reports(name, **kwargs):
...     print(f"***** Report for {name} *****")
...     for key, value in kwargs.items():
...         print(f"### {key}: {value}")
...     print("***** Report End *****n")
... 
... scores = {"John": {"math": 99, "phys": 97},
...           "Jan": {"math": 94, "bio": 98}}
... 
>>> for name, scores in scores.items():
...     generate_score_reports(name, **scores)
... 
***** Report for John *****
### math: 99
### phys: 97
***** Report End *****


***** Report for Jan *****
### math: 94
### bio: 98
***** Report End *****

However, in most cases, you don’t need to use *args or **kwargs. Although it can make your declaration a bit cleaner, it hides the function’s signature. In other words, the users of your functions have to figure out exactly what parameters your functions take. So my advice is to avoid using them if you don’t have to. For instance, can I use a dictionary argument to replace the **kwargs? Similarly, can I use a list or tuple object to replace *args? In most cases, these alternatives should work without any problems.

但是,在大多数情况下,您不需要使用*args**kwargs 。 尽管它可以使声明更清晰,但它会隐藏函数的签名。 换句话说,函数的用户必须准确地确定函数采用的参数。 因此,我的建议是避免在不需要时使用它们。 例如,我可以使用字典参数来替换**kwargs吗? 同样,我可以使用列表或元组对象替换*args吗? 在大多数情况下,这些替代方法应该可以正常工作。

9.参数的类型注释 (9. Type Annotation for Arguments)

As mentioned previously, Python is a dynamically-typed programming language as well as an interpreted language, the implication of which is that Python doesn’t check code validity, including type compatibility, during coding time. Until your code actually executes, will type incompatibility with your function (e.g., send a string to a function when an integer is expected) emerge.

如前所述,Python是一种动态类型的编程语言,也是一种解释型语言,其含义是Python在编码期间不会检查代码的有效性,包括类型兼容性。 在您的代码实际执行之前,将出现与您的函数不兼容的类型(例如,在期望整数时将字符串发送给函数)。

For these reasons, Python doesn’t enforce the declaration of the type of input and output arguments. In other words, when you create your functions, you don’t need to specify what types of parameters they should have. However, it has become possible to do that in recent Python releases. The major benefit of having type annotation is that some IDEs (e.g., PyCharm or Visual Studio Code) could use the annotations to check the type compatibility for you, so that when you or other users use your functions you can get proper hints.

由于这些原因,Python不会强制执行输入和输出参数类型的声明。 换句话说,在创建函数时,无需指定它们应具有的参数类型。 但是,在最近的Python版本中可以做到这一点。 具有类型注释的主要好处是某些IDE(例如PyCharm或Visual Studio Code)可以使用注释来检查类型的兼容性,以便当您或其他用户使用您的函数时,您可以获得正确的提示。

Another related benefit is that if the IDEs know the type of parameter, it can give proper auto-completion suggestions to help you code faster. Certainly, when you write docstrings for your functions, these type annotations will also be informative to the end developers of your code.

另一个相关的好处是,如果IDE知道参数的类型,它可以给出适当的自动完成建议,以帮助您更快地进行编码。 当然,当您为函数编写文档字符串时,这些类型注释也将为代码的最终开发人员提供信息。

Image for post
Helpful Information With Type Annotation (PyCharm)
带有类型注释的有用信息(PyCharm)

10.负责文件 (10. Responsible Documentation)

I equate good documentation with responsible documentation. If your functions are for private uses, you don’t have to write very thorough documentation — you can make the assumption that your code tells the story clearly. If anywhere requires some clarification, you can write a very brief comment that can serve as a reminder for yourself or other readers when your code is revisited. Here, the discussion of responsible documentation is more concerned with the docstrings of your function as public APIs. The following aspects should be included:

我把好的文档与负责的文档等同起来。 如果您的函数是供私人使用的,则不必编写非常详尽的文档-您可以假设您的代码清楚地讲述了故事。 如果任何地方都需要澄清,您可以写一个简短的注释,当您重新访问代码时,它可以提醒您自己或其他读者。 在这里,有关负责任文档的讨论更多地与作为公共API的函数的文档字符串有关。 应包括以下方面:

  • A brief summary of the intended operation of your function. This should be very concise. In most cases, the summary shouldn’t be more than one sentence.

    功能预期操作的简短摘要。 这应该非常简洁。 在大多数情况下,摘要不应超过一个句子。

  • Input arguments: Type and explanation. You need to specify what type of your input arguments should be and what they can do by setting particular options.

    输入参数:类型和说明。 您需要指定输入参数的类型,以及通过设置特定选项可以执行的操作。

  • Return Value: Type and explanation. Just as with input arguments, you need to specify the output of your function. If it doesn’t return anything, you can optionally specify None as the return value.

    返回值:类型和说明。 与输入参数一样,您需要指定函数的输出。 如果不返回任何内容,则可以选择将None指定为返回值。

结论 (Conclusions)

If you’re experienced with coding, you’ll find out that most of your time is spent on writing and refactoring functions. After all, your data usually doesn’t change too much itself— it’s the functions that process and manipulate your data. If you think of data as the trunk of your body, functions are the arms and legs that move you around. So, we have to write good functions to make our programs agile.

如果您有编码经验,您会发现大部分时间都花在编写和重构函数上。 毕竟,您的数据本身通常不会发生太大变化,而是处理和操纵数据的功能。 如果您将数据视为身体的躯干,那么功能就是手臂和腿部。 因此,我们必须编写好的函数来使我们的程序敏捷。

I hope that this article has conveyed some useful information that you can use in your coding.

我希望本文传达了一些可以在编码中使用的有用信息。

Thanks for reading.

谢谢阅读。

翻译自: https://medium.com/better-programming/advanced-python-consider-these-10-elements-when-you-define-python-functions-61c0be8a10ed

python函数内定义函数

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_26711867/article/details/108499137
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-05-15 18:38:50
  • 阅读 ( 1140 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢