Python现在是世界上最流行的语言之一。凭借其多功能性、易于理解的语法和强大的社区支持,它已成为那些刚开始编程之旅的人的首选语言。

与其他面向初学者的编程语言不同,Python之所以真正强大,是因为它能做的太多了。就像JavaScript因其在任何地方都能使用的能力而臭名昭著一样,Python也可以用于:

  • 网站开发(后端)
  • 数据分析和可视化
  • 人工智能和机器学习
  • 科学计算
  • 数值模拟
  • 自动化和脚本编写
  • 游戏开发

但是,仅仅学习Python和掌握它之间是有区别的。

如果你想自成为一名高级开发者,并且Python是你的首选编程语言,你需要了解这些概念并深入理解他们。

1. 面向对象编程 (OOP)

面向对象编程可能是你在编程旅程中需要学习的最重要的概念之一。尽管Python不像Java那样是一个严格的基于类的编程语言,但你确实需要了解良好的OOP概念和最佳实践,才能在现实世界项目中做出贡献。

面向对象编程不仅仅是创建类和对象,它是关于以一种在现实世界中有意义的方式构建你的代码。

2. 一等函数和高阶函数

在Python中,函数被视为一等公民。这意味着,像任何其他对象(例如:整数,字符串或浮点数)一样,函数也可以作为其他函数参数传递,可以从另一个函数返回。

高阶函数是一个可以接收一个或多个函数作为参数,并返回一个函数作为结果的函数。

正确使用高阶函数,可以:

  1. 增加代码可重用性:高阶函数通过允许开发人员创建可以操作其他函数的通用函数,促进代码重用。这导致代码更干净,更易于维护。
  2. 函数式编程技术:理解高阶函数对于利用Python的函数式编程能力至关重要,例如使用map()filter()reduce()。这些内置函数允许简洁和富有表现力的数据操作。
  3. 灵活性和抽象:通过使用高阶函数,开发人员可以抽象复杂的操作,使他们的代码更灵活,更容易适应。例如,传递不同的函数作为参数可以在不修改核心逻辑的情况下改变行为。

代码示例:

def apply_operation(operation, x, y):
    return operation(x, y)

def add(x, y):
    return x + y

def multiply(x, y):
    return x * y

result_add = apply_operation(add, 3, 4) 
result_multiply = apply_operation(multiply, 3, 4) 

print(result_add)       
print(result_multiply)  

3. 闭包

与其他编程语言(例如JavaScript)类似,Python中的闭包允许函数“记住”并访问其封闭范围中的变量,即使外部函数已经执行完毕。

这是教科书定义,在我看来,可以这样理解:

当以下条件满足时,会创建闭包:

  1. 在外部函数中定义了一个嵌套函数。
  2. 嵌套函数引用了外部函数范围中的变量。
  3. 外部函数返回了嵌套函数。

这允许嵌套函数“封闭”它需要的变量,保留它们的状态。

示例:

def outer_function(x):
    def inner_function(y):
        return x + y
    return inner_function

closure = outer_function(10)
result = closure(5)
print(result)  

在上面的例子中,当你调用outer_function时,它返回了一个inner_function对象。注意内部函数将x加到你传递给它的任何数字上。

现在当你调用闭包(不过是inner_function对象)时,你会得到最终值15

现在它可能看起来很无聊,为什么要那样做?有什么意义?

但这确实是一个非常重要的概念,它在很多现实世界的地方都有用。

  1. 创建事件处理程序
  2. 通过数据封装维护状态
  3. 实现装饰器(闭包是装饰器背后的机制)

4. 装饰器

如果你在任何现实世界项目中使用过Python,你可能会知道Python装饰器的强大之处。深入理解Python装饰器在我看来对于任何中级或高级Python开发者都是必不可少的。

简单来说,装饰器是一个接受另一个函数作为参数,扩展其行为并返回一个新函数的函数。这允许在不修改原始函数本身的情况下动态添加额外的功能。

Python中创建装饰器的基本示例:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Before the function call")
        result = func(*args, **kwargs)  # 执行被装饰的函数
        print("After the function call")
        return result
    return wrapper

# 使用装饰器
@my_decorator
def say_hello():
    print("Hello, world!")

say_hello()

输出

Before the function call
Hello, world!
After the function call

如果要创建带参数的装饰器,需要在原始装饰器外层再嵌套一层函数。

def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(3)  # 传递参数
def greet():
    print("Hello!")

greet()

多个装饰器可以按顺序应用于同一个函数,装饰器从里到外依次应用。示例如下:

def uppercase(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

def exclaim(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + "!!!"
    return wrapper

@exclaim
@uppercase
def greet(name):
    return f"Hello, {name}"

print(greet("Alice"))

5. 迭代器,可迭代对象和生成器

在Python中,迭代器和可迭代对象是两个不同但相关的工具,它们共同帮助你一次迭代一个数据流或容器中的数据。迭代器控制迭代过程,而可迭代对象通常持有你想要一次迭代一个值的数据。

在Python中,迭代器和可迭代对象是基础概念,学习如何通过实现迭代器模式创建迭代器对象对于任何Python开发者都是必要的。

迭代器负责两个主要动作:

  1. 返回来自流或容器的数据一次一个项目
  2. 跟踪当前已访问的项目

迭代协议

任何对象,要被视为迭代器,需要实现以下两个特殊方法:

  • __iter__():初始化迭代器,必须返回一个迭代器对象
  • __next__():获取数据流中的下一个迭代器对象

实现迭代器的示例:

class Counter:
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        return self

       def __next__(self):
        if self.current > self.high:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1

for num in Counter(1, 5):
    print(num)

生成器函数

生成器非常迷人。它们让你有灵活性将任何函数转换为迭代器(一个非常简单的定义)。

Python生成器是一种特殊类型的函数,允许你以简单高效的方式创建迭代器。

与返回单个值并立即终止的常规函数不同,生成器使用yield关键字随时间产生一系列值,每次产生后暂停执行。

这意味着当你调用一个生成器函数时,它不会立即运行函数体;相反,它返回一个可以迭代以一次产生值的生成器对象。

这种方法在处理大型数据集或数据流时特别有用,因为它允许你按需生成值而不会消耗大量内存。

解释概念的基本示例:

def count_up_to(n):
    count = 1
    while count <= n:
        yield count
        count += 1

for number in count_up_to(5):
    print(number)

6. 上下文管理器

如果你有任何编程经验,你知道我们经常需要与外部服务交互。无论是文件还是数据库。与任何外部服务工作的一个重要部分是正确分配和释放资源。

如果你刚开始学习任何语言的编程,你会认识到这个模式:

file = open('example.txt', 'w')
try:
    file.write('Hello, World!')
finally:
    file.close()

你打开一个文件,与它交互,然后最重要的是关闭它

这是基本的分配和释放过程。当涉及到数据库连接时,你也会做类似的操作。工作完成后关闭连接。

上下文管理器使这个过程变得简单。上下文管理器是实现了__enter____exit__两个特殊函数的对象。当你使用with关键字进入上下文管理器时,执行分配或__enter__部分,当你退出时,执行释放或__exit__部分。

所以如果你需要与文件交互,使用上下文管理器,它看起来像这样:

with open('example.txt', 'w') as file:
    file.write('Hello, World!')

7. 内存管理

Python是一种高级编程语言。这意味着,大部分复杂性都从你这里抽象出来,以便你可以专注于应用程序的业务逻辑。

这些抽象之一是Python的内存管理。Python通过其垃圾收集过程负责分配和释放内存,所以你不必每次使用后都释放内存。

但是,你在编程旅程中越是高级,你就越想要控制权,这就是为什么学习Python中的内存管理概念是必要的。这可以帮助你在调试复杂问题时,在查找应用程序中的内存泄漏以及更好地理解幕后发生的事情。

不深入细节,以下是你应该了解的关键概念:

1. 内存分配

Python通过一个私有堆来管理内存,这是专门用于Python对象和数据结构的内存部分。Python内存管理器负责分配和释放这部分内存。

  • 静态内存:为在运行时不改变的变量分配,通常存储在栈中。
  • 动态内存:在运行时分配,用于大小可能变化的对象,通常存储在堆中。

2. 内存池

Python使用内存池系统来有效管理小块内存。当创建一个对象时,Python检查池中是否有可用的块可以容纳它。如果没有,它将从操作系统请求额外的内存。这有助于减少与频繁分配和释放小块内存相关的开销。

3. 引用计数和垃圾收集

Python使用两种主要技术来管理内存:

  • 引用计数:每个对象都跟踪有多少引用指向它。当引用计数降至零时,对象可以被安全地释放。
  • 垃圾收集:为了处理循环引用(两个或多个对象相互引用),Python使用垃圾收集器定期检查未引用的对象并释放它们的内存。

4. 代际垃圾收集

Python的垃圾收集器根据对象的生命周期将对象分类为几代:

  • 新生代:用于新创建的对象。
  • 老年代:用于在多次垃圾收集周期中存活的对象。

这种代际方法通过专注于更有可能迅速变得不可达的年轻对象来优化性能。

8. 并发

除非你正在处理永远不会有超过一个用户(你)或一次运行一个任务的个人项目,否则你需要考虑并发。

根据字典定义,并发意味着同时运行多个任务。有不同的方式可以做到这一点,不同的并发风味有细微的差别,只有当你深入了解它们的内部工作时才会变得清晰。

不同的风味包括:多线程,多进程和异步编程(asyncio)。

但要记住,只有一个(多进程)真正同时运行进程。

Python并发的关键主题:

  1. 线程
    threading模块是Python运行并发任务的最便捷方式之一。然而,由于GIL(全局解释器锁,看下一点),一次只能有一个线程执行Python字节码,这限制了真正的并行性。尽管如此,线程对于文件处理或网络请求等I/O密集型任务仍然有效。这里的关键概念包括管理锁,了解GIL的影响,以及正确使用线程池(例如,concurrent.futures.ThreadPoolExecutor)。
  2. 多进程
    与线程不同,multiprocessing模块允许Python程序通过创建单独的进程来绕过GIL,每个进程都有自己的解释器和内存空间。这种方法实现了真正的并行性,非常适合CPU密集型任务。然而,进程间通信和内存使用需要仔细管理,通常需要更深入地了解共享内存、队列和用于管理工作进程池的multiprocessing.Pool类等主题。
  3. 异步编程
    随着asyncio和其他异步库如aiohttpasyncpg的引入,Python现在对异步编程有了强大的支持。这种方法特别适合I/O密集型和高延迟任务,如处理多个网络请求。理解async/await语法、事件循环和任务调度对于掌握异步代码至关重要。这种模型与线程和多进程不同,它依赖于非阻塞调用,允许多个任务在单个线程上似乎并行运行。
  4. 并发库和框架
    对于更高级的使用,Python提供了简化并发管理的库,如concurrent.futures用于抽象线程和多进程,以及asyncio用于原生异步支持。像geventTwisted这样的工具提供了处理高并发网络应用的替代方法,可能值得探索。

Python并发的挑战:

Python并发带来了诸如处理GIL、管理资源争用(例如,死锁、竞态条件)和调试并发代码等挑战。此外,每种并发模型都引入了自己的权衡,要求开发人员根据工作负载是CPU密集型还是I/O密集型来选择正确的方法。

9. 全局解释器锁 (GIL)

软件工程是一个权衡的游戏。

GIL或全局解释器锁是标准Python解释器(CPython版本)中的一个机制,只允许一个线程一次执行Python字节码。这意味着即使在多线程Python程序中,也只有一个线程可以在任何给定时刻在解释器中执行。虽然GIL简化了内存管理并使CPython更加稳定,但它限制了Python在CPU密集型程序中执行真正的并行性的能力。

但为什么需要GIL呢?

Python的内存管理默认不是线程安全的,所以为了防止这种情况并有效管理内存,CPython版本中引入了GIL。GIL的实现使内存管理变得更简单,因为它简化了引用计数,这是Python内存管理过程的核心。但这也确实限制了Python在CPU密集型任务中执行真正的并行性的能力。

与此相关的一些概念。

  1. 理解GIL对并发的影响,无论是I/O密集型还是CPU密集型任务
  2. 线程限制:了解GIL如何影响Python的threading模块是决定何时使用线程与进程的关键。
  3. 多进程:熟悉multiprocessing模块,它提供不受GIL限制的基于进程的并行性。
  4. Asyncio和非阻塞I/O:对于I/O密集型任务,asyncio模块提供了一种无需线程即可实现并发的方式,绕过了许多与GIL相关的问题。
  5. 其他Python实现:PyPy、Jython和IronPython是其他解释器,它们不使用GIL或有不同的实现,可能为特定用例提供性能优势。

10. 协程和异步编程

作为一种编程范式,异步编程旨在高效使用资源。

想象一下,有一家餐厅的排队,每个人点菜、付款并接收他们的订单,然后才从队列中移动。这是同步执行,即使他们只是在等待,一个任务也会阻塞其他任务的执行。

现在在另一家餐厅,你排队,点菜并拿到收据,然后移动到另一个队列等待,这意味着你不阻塞下一个人的排队。这非常非常表面地,就是我们想要用异步编程做的事情。

你应该了解的异步编程主题(与语言无关的概念):

  1. 理解阻塞和非阻塞任务之间的区别
  2. 事件循环
  3. 回调和Promises/Futures
  4. 协程
  5. 异步上下文中的错误处理

在Python中,异步编程通过asyncio库得到支持。你用async关键字定义一个函数,将其转换为协程,以便它可以在异步上下文中执行。

要执行一个async函数(协程),你通常在另一个异步函数内使用await,或者你可以在一个事件循环内运行它(例如,使用asyncio.run()

作为一个Python开发者,深入理解这些概念将给你带来巨大的优势。

本文由mdnice多平台发布


Miniwa
29 声望1 粉丝