也有提供此功能的社区包,例如Click和 。

的函数

函数(也称为匿名函数)是没有名称且只有一个表达式作为其主体的小型函数。

在中,它们是使用关键字定义的:

lambda  : 

主体必须是单个表达式,而不是语句。

这很重要:表达式返回值,语句不返回。

最简单的函数示例是将数字的值加倍:

lambda num : num * 2

函数可以接受更多参数:

lambda a, b : a * b

无法直接调用函数,但您可以将它们分配给变量:

multiply = lambda a, b : a * b

print(multiply(22))  # 4

函数的实用性在于与其它功能结合使用,例如结合map()、()和()。

递归

中的函数可以调用自身,这就是递归。递归在许多情况下都非常有用。

解释递归的常用方法是实现阶乘计算。

一个数字n的阶乘是数字n乘以n-1,再乘以n-2,以此类推,直到数字1:

3! = 3 * 2 * 1 = 6
4! = 4 * 3 * 2 * 1 = 24
5! = 5 * 4 * 3 * 2 * 1 = 120

使用递归,我们可以编写一个计算任意数阶乘的函数:

def factorial(n):
    if n == 1return 1
    return n * factorial(n-1)

print(factorial(3))  # 6
print(factorial(4))  # 24
print(factorial(5))  # 120

如果在 () 函数中调用(n)而不是(n-1),则会导致无限递归。默认情况下,将在1000次调用时停止递归,此时您将收到错误。

递归在很多地方都有用,它可以帮助我们在没有其它更好方法的情况下简化代码,所以了解这种技术是件好事。

嵌套函数

中函数可以嵌套在其它函数中。

在函数内部定义的函数仅在该函数内可见。

这对于创建在函数内有用,但在函数外无用的程序很有用。

您可能会问:如果它没有害处,我为什么要“隐藏”这个功能?

因为最好隐藏函数本地并且在其它地方没有用的功能。

另外,这样我们可以使用闭包(稍后会详细介绍)。

这里是一个例子:

def talk(phrase):
    def say(word):
        print(word)

    words = phrase.split(' ')
    for word in words:
        say(word)

talk('I am going to buy the milk')

如果要从内部函数访问外部函数中定义的变量,首先需要将其声明为:

def count():
    count = 0

    def increment():
        nonlocal count
        count = count + 1
        print(count)

    increment()

count()

这对闭包特别有用,我们将在接下来的说明中看到。

闭包

如果函数返回一个嵌套函数,则该嵌套函数可以访问在该函数中定义的变量,即使该函数不再处于运行状态。

这是一个简单的计数器示例。

def counter():
    count = 0

    def increment():
        nonlocal count
        count = count + 1
        return count

    return increment

increment = counter()

print(increment())  # 1
print(increment())  # 2
print(increment())  # 3

我们返回()这个内部函数,即使()函数已经结束,仍然可以访问count变量的状态。

装饰器

装饰器是一种可以以任何方式增强或改变函数工作方式的方法。

装饰器是用@符号定义的,@后面跟装饰器名称,(装饰器用在)在函数定义之前。

例子:

@logtime
def hello():
    print('hello!')

这个hello函数分配了装饰器。

每当我们调用hello()时,装饰器也都会被调用。

装饰器是一个以函数为参数的函数,它将(被装饰的)函数包装在内部函数中,该内部函数执行必须完成的工作,然后返回这个内部函数。换句话说:

def logtime(func):
    def wrapper():
        # do something before
        val = func()
        # do something after
        return val
    return wrapper

文档字符串

文档非常重要,不仅可以用于告知其他人(自己写的)函数/类/方法/模块的目标是什么,还可以帮助您(在较长时间后)理解自己的代码。

当您在6或12个月后会看您的的代码时,可能不记得写代码时脑海中的所有想法。这个时候阅读您的代码并理解它在做什么将变得非常困难。

注释是帮助自己(和他人)摆脱这种困境的一种方式:

# this is a comment

num = 1  # this is another comment

另一种方法是使用。

文档字符串的实用性在于它们遵循约定,因此它们可以被自动处理。

这是您为函数定义文档字符串的方式:

def increment(n):
    """Increment a number"""
    return n + 1

这是为类和方法定义文档字符串的方式:

class Dog:
    """A class representing a dog"""
    def __init__(self, name, age):
        """Initialize a new dog"""
        self.name = name
        self.age = age

    def bark(self):
        """Let the dog bark"""
        print('WOF!')

通过在文件顶部放置一个文档字符串来解释记录一个模块,例如,假设这是dog.py:

"""Dog module

This module does ... bla bla bla and provides the following classes:

- Dog
...
"""


class Dog:
    """A class representing a dog"""
    def __init__(self, name, age):
        """Initialize a new dog"""
        self.name = name
        self.age = age

    def bark(self):
        """Let the dog bark"""
        print('WOF!')

文档字符串可以跨越多行:

def increment(n):
    """Increment
    a number
    """

    return n + 1

将处理这些(文档字符串),您可以使用全局函数help()来获取类/方法/函数/模块的文档。

例如调用help()会给返回这个:

Help on function increment in module
__main__:

increment(n)
    Increment
    a number

格式化文档字符串有许多不同的标准,您可以选择遵守自己喜欢的标准。

我喜欢谷歌的标准:#38–and-

(遵循)标准可以使用工具来提取文档字符串并自动为您的代码生成文档。

反射

可以使用反射来分析函数、变量和对象。

首先,使用全局函数help()(如果以文档字符串的形式提供)我们可以获得文档。

然后,您可以使用print()获取有关函数的信息:

def increment(n):
    return n + 1

print(increment)


或者(获取)对象(的信息):

class Dog():
    def bark(self):
        print('WOF!')

roger = Dog()

print(roger)


我们可以使用type()函数获取对象的类型:

print(type(increment))


print(type(roger))


print(type(1))


print(type('test'))

全局函数dir()可以找出对象的所有方法和属性:

print(dir(roger))

# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bark']

全局函数id()显示任意对象在内存中的位置:

print(id(roger))  # 140227518093024
print(id(1))  # 140227521172384

这对于检查两个变量是否指向同一个对象会很有用。

标准库模块为我们提供了更多获取对象信息的工具,您可以在这里查看

注解

是动态类型的,我们不必指定变量、函数参数或函数返回值的类型。

注解允许我们(可选地)这样做。

这是一个没有注解的函数:

def increment(n):
    return n + 1

这是带有注解的相同函数:

def increment(n: int) -> int:
    return n + 1

您还可以注解变量:

count: int = 0

将忽略这些注解。一个名为mypy的工具可以独立运行,也可以集成到VS Code或等IDE中,以便在您编码时自动检查静态类型错误。它还将帮助您在运行代码之前捕获类型不匹配的错误。

这是一个很大的帮助,尤其是当您的软件规模变得很大并且需要重构代码时。

异常

处理错误很重要,为我们提供了异常处理来做到这一点。

如果将代码行包装到try:块中:

try:
    # 一些代码

如果发生错误,会提醒您,您可以使用块确认发生了哪种错误:

try:
    # 一些代码
except :
    # 处理 
except :
    # 处理 

要捕获所有异常,您可以使用不包含任何错误类型的块:

try:
    # 一些代码
except :
    # 处理 
except:
    # 捕获其它所有错误

如果没有发现异常,则将运行else块:

try:
    # 一些代码
except :
    # 处理 
except :
    # 处理 
else:
    # 没有抛出异常,代码成功运行

块允许您在任何情况下执行某些操作,无论是否发生错误:

try:
    # 一些代码
except :
    # 处理 
except :
    # 处理 
else:
    # 没有抛出异常,代码成功运行
finally:
    # 任何情况下都将运行的代码

将发生的具体错误取决于您正在执行的操作。

例如,如果您正在读取一个文件,可能会得到一个。如果您将一个数除以零,将会得到一个。如果发生类型转换问题,您可能会得到一个。

试试这个代码:

result = 2 / 0
print(result)

程序将因错误而终止:

Traceback (most recent call last):
  File "main.py", line 1, in 
    result = 2 / 0
ZeroDivisionError: division by zero

并且错误(代码行)之后的代码将不会被执行。

在try:块中添加该操作可以让我们优雅地恢复(错误)并继续执行程序:

try:
    result = 2 / 0
except ZeroDivisionError:
    print('Cannot divide by zero!')
finally:
    result = 1

print(result)  # 1

您也可以在自己的代码中使用raise语句引发异常:

raise Exception('An error occurred!')

这会抛出一个异常,您可以使用以下方法拦截它:

try:
    raise Exception('An error occurred!')
except Exception as error:
    print(error)

您还可以扩展来定义自己的异常类:

class DogNotFoundException(Exception):
    pass

这里pass的意思是“什么都没有”,当我们定义一个没有方法的类或没有代码的函数时,我们必须使用它。

try:
    raise DogNotFoundException()
except DogNotFoundException:
    print('Dog not found!')

中with语句

with语句对于简化异常处理非常有帮助。

例如,在处理文件时,每次打开文件都必须记得关闭它。

with使这个过程变得透明(即对程序员不可见)。

(使用with)可以不像下面这样写:

filename = '/Users/flavio/test.txt'

try:
    file = open(filename, 'r')
    content = file.read()
    print(content)
finally:
    file.close()

您可以这样写:

filename = '/Users/flavio/test.txt'

with open(filename, 'r'as file:
    content = file.read()
    print(content)

换句话说,有内置的隐式异常处理,其会自动为我们调用close()。

上面的例子只是为了介绍with的功能,而不是说它仅在处理文件方面对我们有帮助。

如何使用pip安装第三方包

标准库包含大量实用的程序,可以简化我们的开发需求,但是没有什么能满足_一切_。

这就是个人和公司创建第三方包,并将它们作为开源软件提供给整个社区的原因。

这些模块都收集在一个地方,可在 获得包索引,并且可以使用pip将它们(第三方模块)安装在您的系统上。

在撰写本文时,有超过270,000个免费第三方包可供我们使用。

如果您按照安装说明进行操作,您应该已经安装了 pip。

使用命令pip 可以安装任何第三方包:

pip install 

或者,如果您确实遇到了问题,也可以通过 -m运行它:

python -m pip install 

例如,您可以安装 包,这是一个流行的 HTTP 库:

pip install requests

一旦这样做,它就可以用于您所有的脚本,因为包是全局安装的。

(第三方包安装的)具体位置取决于您的操作系统。

在运行 3.9的macOS上,位置是///.//3.9/lib/.9/site-。

使用以下命令将第三方包升级到最新版本:

pip install –U 

使用以下命令安装指定版本的第三方包:

pip install ==

使用以下命令卸载一个第三方包:

pip uninstall 

使用以下命令显示已安装第三方包的详细信息,包括版本、文档网站和作者信息:

pip show 

列表推导式

列表推导式以一种非常简洁的方式创建列表。

假设有一个列表:

numbers = [12345]

您可以使用列表推导式创建一个新列表,该列表由列表元素的2次幂组成:

numbers_power_2 = [n**2 for n in numbers]
# [1, 4, 9, 16, 25]

列表推导是一种有时比循环更受欢迎的语法,因为当(有些)操作写在一行时其更具可读性:

numbers_power_2 = []
for n in numbers:
    numbers_power_2.append(n**2)

同样有时也比map()更好:

numbers_power_2 = list(map(lambda n : n**2, numbers))

多态

多态将一个功能泛化,因此它可以在不同的类型上工作。多态是面向对象编程中的一个重要概念。

我们可以在不同的类上定义相同的方法:

class Dog:
    def eat():
        print('Eating dog food')

class Cat:
    def eat():
        print('Eating cat food')

然后我们可以生成对象,无论对象属于哪个类,我们都可以调用eat()方法,但是会得到不同的结果:

animal1 = Dog()
animal2 = Cat()

animal1.eat()
animal2.eat()

我们构建了一个通用接口,不需要知道动物是猫还是狗。

译者:这个例子不太好,不完整,看下面这个例子:

In [1]: class Animal:
   ...:     def eat(self):
   ...:         print("animal eating ...")
   ...: 

In [2]: class Dog(Animal):
   ...:     def eat(self):
   ...:         print("dog eating ...")
   ...: 

In [3]: class Cat(Animal):
   ...:     def eat(self):
   ...:         print("cat eating ...")
   ...: 

In [4]: a = Animal()

In [5]: d = Dog()

In [6]: c = Cat()

In [7]: a.eat()
animal eating ...

In [8]: d.eat()
dog eating ...

In [9]: c.eat()
cat eating ...

译者:多态实际上是看运行时对象具体的类型,在Java中,是可以这样写的 dog = new Dog(),即创建一个对象dog,这是编译时,但是在运行时dog.eat()打印dog …

运算符重载

运算符重载是一种先进的技术,我们可以用来使类具有可比性,并使它们与运算符一起工作。

让我们来创建一个类Dog:

class Dog:
    # the Dog class
    def __init__(self, name, age):
        self.name = name
        self.age = age

创建两个Dog对象:

roger = Dog('Roger'8)
syd = Dog('Syd'7)

我们可以使用运算符重载添加一种基于age属性的方法来比较这两个对象:

class Dog:
    # the Dog class
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __gt__(self, other):
        return True if self.age > other.age else False

现在如果您尝试运行print(roger > syd),将得到结果True。

与我们定义()(大于)的方式相同,我们可以定义以下方法:

然后还有使用算术运算符操作的方法:

还有其它几种方法可以与运算符一起使用,但您应该明白了(这种思想)。

虚拟环境

在您的系统上运行多个应用程序是很常见的。

当应用程序需要相同的模块时,有时您会遇到一个棘手的情况,即一个应用程序需要一个版本模块,而另一个应用程序需要该模块的不同版本。

您可以使用虚拟环境解决这个问题。

我们将使用venv。其它工具的工作方式类似,例如。

如下创建虚拟环境

python -m venv .venv

(该命令)在您要开始的项目的文件夹或者现有项目的文件夹(的根目录下运行)。

译者:项目的根目录即其本身,例如一个项目fCC在/Users/abc//fCC,那么该项目的根目录就是/Users/abc//fCC/

然后运行

source .venv/bin/activate

(如果是)在Fish shell上,使用 .venv/bin/.fish

执行这个命令将激活虚拟环境。根据您的配置,可能还会看到终端提示发生变化。

我的从

变成

(.venv)

现在运行pip将使用这个虚拟环境而不是全局环境。

总结

非常感谢您阅读本书。

我希望它能鼓励您更多去地了解 。

有关和一般编程教程的更多信息,请查看我的博客 .com。

你可以发送邮件至 @.com提供任何反馈、勘误或意见。

请注意:您可以获取此手册的PDF、ePub和Mobi版本

原文链接:

加入IP合伙人(站长加盟) | 全面包装你的品牌,搭建一个全自动交付的网赚资源独立站 | 晴天实测8个月运营已稳定月入3W+
限时特惠:本站每日持续更新海量内部创业教程,一年会员只需98元,全站资源免费无限制下载点击查看会员权益

站长微信: qtw123cn

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注