C#开发者快速学习python(4):与C#不同点,鸭子类型,多线程,多进程编程,访问控制,事件委托实现,lambda表达式,常用内置库,为啥说python简单易用?

勇哥注:

这个系列贴子,勇哥站在C#开发角度来学习python。

我以C#常用知识点来类比python,共4篇。

你看完后,就明白,所谓python简单易学只是表面。这门语言的语法体量很大不输给C#,它其实是很好的把复杂性隐藏了,给人入门简单的印象,其实想精通它困难是比较大的。


C#开发者快速学习python(1):入口函数,运算符,模块与包,py文件 ,基础数据类型,列表

C#开发者快速学习python(2):枚举,装饰器,self,类的三种类型,特殊方法,oop,代码格式,方法 ,方法返回类型,识别函数返回类型

C#开发者快速学习python(3):有无数组?字符串方法,读写文件,序列化,配置文件,异常处理,循环和选择,模块与包,调试手段

C#开发者快速学习python(4):与C#不同点,鸭子类型,多线程,多进程编程,访问控制,事件委托实现,lambda表达式,常用内置库,为啥说python简单易用?



(九)python的语法体系很大(和C#相比,该有的都有了),凭什么说其“简单易用”?

你说得非常对!Python虽然常被贴上"简单易学"的标签,但它的语法体系和功能实际上相当丰富和复杂。

这种表面简洁与内在丰富的结合,正是Python的魅力所在。下面从几个角度展开说明:


1. Python的"简单"是一种设计哲学

Python的"简单"更多体现在它的语法风格和设计理念上:

- 可读性优先 :强制缩进、简洁的关键字、清晰的代码结构

- 一种明确的做事方式 :"There should be one—and preferably only one—obvious way to do it"

- 语法糖 :通过简洁的语法表达复杂逻辑(如列表推导式、上下文管理器)

但这种"简单"并不意味着功能有限,相反,Python通过这些设计哲学,

让复杂的功能变得易于表达和理解。


2. Python语法体系的复杂性体现在 

2.1 多范式支持

Python支持面向对象、函数式、过程式等多种编程范式,每种范式都有对应的语法特性:

- 面向对象 :类、继承、多态、属性装饰器、元类

- 函数式 :高阶函数、lambda表达式、闭包、装饰器

- 过程式 :直观的控制流语句 2.2 动态类型系统的深度

Python的动态类型系统提供了灵活性,但也带来了复杂性:

- 动态类型检查与运行时错误处理

- 类型提示(Type Hints)系统的不断完善

- 鸭子类型的灵活应用 2.3 高级特性的丰富性

Python包含许多强大的高级特性:

- 装饰器 :函数和类装饰器,可以在不修改原函数的情况下扩展功能

- 上下文管理器 :通过 with 语句简化资源管理

- 生成器与协程 :支持惰性计算和异步编程

- 元编程 :通过元类、反射等机制在运行时修改代码行为

- 描述符协议 :自定义属性访问行为 2.4 内置数据结构的强大功能

Python的内置数据结构(列表、字典、集合等)不仅使用简单,而且提供了丰富的方法和操作:

- 列表推导式、字典推导式、集合推导式

- 切片操作的灵活应用

- 字典的高级用法(默认字典、有序字典等)


3. 与C#的语法体系对比

虽然Python和C#在功能上有很多对应之处,但它们的实现方式和设计理念有显著差异:


- 类型系统 :Python是动态弱类型,C#是静态强类型

- 访问控制 :Python依赖命名约定,C#使用严格的访问修饰符

- 错误处理 :Python倾向于"请求原谅比许可容易"(EAFP),C#倾向于"先看后跳"(LBYL)

- 扩展性 :Python通过装饰器和元编程扩展,C#通过接口和特性扩展


4. Python的学习曲线

Python的学习曲线特点是"入门容易,精通难":

- 初学者可以在几小时内掌握基础语法并写出简单程序

- 但要真正掌握Python的高级特性、最佳实践和性能优化,需要相当长的时间和实践

- 随着Python版本的更新,新特性不断增加(如f-string、海象运算符、模式匹配等)

5. 为什么Python依然受欢迎

尽管语法体系复杂,但Python依然保持着极高的受欢迎度,原因在于:

- 表达力强 :用更少的代码表达更复杂的逻辑

- 生态丰富 :拥有庞大的第三方库和活跃的社区

- 跨平台 :可以在各种操作系统上运行

- 应用广泛 :从Web开发、数据分析到人工智能、科学计算

Python就像一把瑞士军刀,虽然结构不复杂,但功能全面且强大。

它的"简单"是一种精心设计的复杂性,让新手容易上手,同时也能满足专业开发者的高级需求。

这种平衡正是Python成功的关键所在。


(八)内置库到底有哪些

Python的内置库(也称为标准库)是随Python安装一起提供的模块和包的集合,功能非常丰富。

这些库覆盖了从基础数据处理到高级系统交互的广泛领域,无需额外安装即可使用。

下面按功能类别介绍主要的内置库:


1. 基础功能模块

1.1 系统交互

- os :提供与操作系统交互的功能(文件操作、目录操作、环境变量等)


  import os

  os.mkdir('new_folder')  # 创建目录

  print(os.getcwd())      # 获取当前工作目录


- sys :提供对Python解释器相关的操作和变量(命令行参数、退出程序等)

  import sys

  print(sys.version)      # 查看

  Python版本

  sys.exit()              # 退出程序

- platform :提供获取平台(操作系统)信息的功能 


1.2 文件与目录处理

- pathlib :提供面向对象的文件系统路径操作


  from pathlib import Path

  path = Path('example.txt')

  if path.exists():

      print(path.read_text())


- shutil :提供高级文件操作(复制、移动、删除等)


2. 数据类型与数据结构 

2.1 基础数据结构扩展

- collections :提供额外的数据类型(OrderedDict、defaultdict、Counter等)


  from collections import 

  defaultdict, Counter

  d = defaultdict(list)   # 默认值为

  列表的字典

  c = Counter([1, 2, 2, 3, 3, 3])  

  # 计数


- heapq :提供堆队列算法(优先队列)

- bisect :提供二分查找算法 2.2 数据序列化

- json :处理JSON数据的编码与解码


  import json

  data = {'name': 'Alice', 'age': 

  30}

  json_str = json.dumps(data)  # 转为JSON字符串

  parsed_data = json.loads

  (json_str)  # 解析JSON字符串


- pickle :Python对象的序列化与反序列化

- csv :处理CSV文件的读写


3. 文本处理 

3.1 字符串操作

- string :提供字符串常量和辅助函数

- re :正则表达式操作(匹配、替换、分割等)

  import re

  pattern = r'\d+'

  result = re.findall(pattern, 

  'There are 123 apples and 456 

  oranges')

  print(result)  # 输出: ['123',  '456']

3.2 文本编码

- codecs :提供编解码器接口

- unicodedata :Unicode字符数据库



4. 日期与时间

- datetime :处理日期和时间

  from datetime import datetime, 

  timedelta

  now = datetime.now()

  tomorrow = now + timedelta

  (days=1)

  print(now.strftime('%Y-%m-%d 

  %H:%M:%S'))  # 格式化输出


- time :提供时间相关的函数

- calendar :日历相关功能

5. 网络与通信 

5.1 网络协议

- socket :底层网络接口

- http.client :HTTP客户端协议实现

- urllib :URL处理模块(请求、解析等)


  from urllib.request import 

  urlopen

  response = urlopen('https://www.python.org')

  data = response.read()


- smtplib :SMTP协议实现(发送邮件)

- email :处理电子邮件消息 5.2 网络服务

- http.server :简单的HTTP服务器

- socketserver :网络服务器框架


6. 并发与并行

- threading :多线程编程


  import threading

  def worker():

      print('Worker thread')

  t = threading.Thread

  (target=worker)

  t.start()

  t.join()


- multiprocessing :多进程编程

- concurrent.futures :高级异步执行接口

- asyncio :异步I/O编程


7. 数学计算

- math :数学函数(三角函数、对数、指数等)

  import math

  print(math.sqrt(16))  # 平方根

  print(math.pi)        # 圆周率


- random :生成随机数

- statistics :统计函数

- decimal :十进制浮点运算

8. 加密与安全

- hashlib :安全哈希算法(MD5、SHA1等)


  import hashlib

  hash_obj = hashlib.sha256

  (b'Hello World')

  print(hash_obj.hexdigest())


- secrets :生成安全的随机数(用于密码、令牌等)

- crypt :Unix密码加密


9. 测试与调试

- unittest :单元测试框架


  import unittest

  class TestStringMethods(unittest.

  TestCase):

      def test_upper(self):

          self.assertEqual('hello'.

          upper(), 'HELLO')


- doctest :文档测试

- pdb :Python调试器

- logging :日志记录


10. 其他常用模块

- argparse :命令行参数解析

- enum :枚举类型支持

- typing :类型提示支持

- itertools :迭代器函数


  import itertools

  # 生成笛卡尔积

  for combo in itertools.product

  ([1, 2], ['a', 'b']):

      print(combo)  # 输出: (1, 'a'), (1, 'b'), (2, 'a'),  (2, 'b')


- functools :函数工具(如lru_cache、partial等)

- contextlib :上下文管理器工具


11. Python标准库的版本变化

Python的标准库会随着版本更新而变化,一些模块可能被添加、改进或废弃。例如:

- Python 3.5 添加了 typing 模块

- Python 3.7 添加了 dataclasses 模块

- Python 3.10 添加了模式匹配(match-case)语法

12. 如何学习和使用内置库

- 官方文档 :Python官方文档是学习标准库的最佳资源,包含详细的API说明和示例

- help()函数 :在Python交互式解释器中使用 help(module_name) 可以查看模块的帮助信息

- 实践探索 :通过实际项目练习使用不同的模块


Python的内置库就像是一个"瑞士军刀",提供了丰富的功能支持各种编程需求。

掌握这些内置库,可以大大提高编程效率,避免重复造轮子。


(七)lambda表达式

Lambda表达式是Python中定义匿名函数的一种简洁方式,也被称为"lambda函数"或"lambda表达式"。下面详细介绍lambda表达式的相关内容:


1. Lambda表达式的基本语法

Lambda表达式的语法格式为: lambda 参数列表: 表达式


- lambda 是Python的关键字

- 参数列表与普通函数类似,可以有多个参数,也可以没有参数

- 冒号 : 用于分隔参数列表和表达式

- 表达式是函数的返回值,不需要使用 return 语句

示例:

# 定义一个lambda函数,接受两个参数并返回它们的和
add = lambda x, y: x + y

# 调用lambda函数
result = add(3, 5)  # 输出: 8

2. Lambda表达式的特点

- 匿名性 :lambda函数没有名称,它返回一个函数对象

- 简洁性 :通常用于编写简单的单行函数

- 表达式单一 :只能包含一个表达式,不能包含复杂的逻辑(如循环、条件语句等)

- 闭包特性 :可以访问定义它的作用域中的变量


3. Lambda表达式的常见用法 

3.1 作为函数参数

lambda表达式常用于需要传递简单函数作为参数的场景:

# 结合内置函数使用
data = [1, 3, 5, 7, 9]
# 使用lambda作为sorted的key参数
result = sorted(data, key=lambda x: -x)  # 降序排序
print(result)  # 输出: [9, 7, 5, 3, 1]

# 使用lambda作为filter的筛选条件
even_numbers = list(filter(lambda x: x % 2 == 0, range(10)))  # 筛选偶数
print(even_numbers)  # 输出: [0, 2, 4, 6, 8]

# 使用lambda作为map的映射函数
squared = list(map(lambda x: x * x, [1, 2, 3, 4]))  # 计算平方
print(squared)  # 输出: [1, 4, 9, 16]

3.2 作为闭包

lambda表达式可以访问其定义环境中的变量:

def make_multiplier(n):
    # 返回一个lambda函数,该函数捕获了变量n
    return lambda x: x * n

# 创建两个乘法函数
double = make_multiplier(2)  # 乘以2的函数
triple = make_multiplier(3)  # 乘以3的函数

print(double(5))  # 输出: 10
print(triple(5))  # 输出: 15

3.3 在列表推导式或字典推导式中

# 列表推导式中使用lambda
funcs = [lambda x: x * i for i in range(5)]
for f in funcs:
    print(f(2))  # 注意:这里会输出5个8,因为lambda捕获的是i的引用,最终i=4

# 改进方法 - 使用默认参数立即绑定值
funcs = [lambda x, i=i: x * i for i in range(5)]
for f in funcs:
    print(f(2))  # 正确输出: 0, 2, 4, 6, 8

3.4 作为回调函数

在GUI编程或异步编程中,lambda常作为简单的回调函数:

# 简化版的GUI按钮回调示例
class Button:
    def __init__(self, callback=None):
        self.callback = callback
    
    def click(self):
        if self.callback:
            self.callback()

# 创建按钮并设置lambda作为回调
button = Button(lambda: print("按钮被点击了!"))
button.click()  # 输出: 按钮被点击了!

4. Lambda表达式与普通函数的对比

- 代码长度 :lambda通常更简洁,适合单行逻辑

- 可读性 :对于复杂逻辑,普通函数更易读,尤其是有文档字符串的情况下

- 使用场景 :lambda适合简单的操作,普通函数适合复杂的、可重用的逻辑

5. Lambda表达式的限制

- 只能包含一个表达式,不能包含多条语句或复杂控制流

- 不能包含return语句(表达式的值自动作为返回值)

- 不能包含文档字符串

- 不能用作类方法的替代

6. 实际应用建议

- 对于简单的、一次性的函数,使用lambda表达式可以使代码更简洁

- 当函数逻辑变得复杂时,应考虑将lambda重构为普通函数以提高可读性

- 在使用lambda作为闭包时,注意变量作用域和延迟绑定的问题

Lambda表达式是Python函数式编程的重要组成部分,它与map()、filter()、sorted()等内置函数结合使用,

可以使代码更加简洁、优雅。


(六)C#的事件、委托,在python中有无相似的概念

C#中的事件(Event)和委托(Delegate)是实现回调机制和观察者模式的重要机制。

Python中虽然没有直接对应的关键字或类型,但可以通过其灵活的函数特性和面向对象设计实现类似功能。

下面详细介绍Python中的相关实现方式:


1. C#委托的Python对应概念

C#中的委托是一种类型安全的函数指针,可以指向一个或多个方法。在Python中,函数是一等公民(first-class citizens),

可以直接作为参数传递、赋值给变量、存储在数据结构中,这提供了类似委托的功能。


基本示例:

# 函数作为参数传递(类似委托)
def add(a, b):
    return a + b

def multiply(a, b):
    return a * b

def calculate(operation, x, y):
    # operation 类似于委托,可以接受不同的函数
    return operation(x, y)

# 使用
result1 = calculate(add, 3, 4)       # 输出: 7
result2 = calculate(multiply, 3, 4)  # 输出: 12

更接近委托的实现 - 使用functools模块:

import functools

# 创建类似委托的函数
add_five = functools.partial(lambda x, y: x + y, 5)  # 固定第一个参数为5

# 调用
result = add_five(10)  # 输出: 15

2. C#事件的Python对应概念

C#中的事件是基于委托的通知机制,允许对象在特定状态改变时通知其他对象。Python中可以通过实现观察者模式来模拟事件系统。

自定义事件系统实现:

class Event:
    def __init__(self):
        self._handlers = []
    
    def add_handler(self, handler):
        # 注册事件处理程序(类似C#的 += 运算符)
        if handler not in self._handlers:
            self._handlers.append(handler)
    
    def remove_handler(self, handler):
        # 移除事件处理程序(类似C#的 -= 运算符)
        if handler in self._handlers:
            self._handlers.remove(handler)
    
    def fire(self, *args, **kwargs):
        # 触发事件(调用所有注册的处理程序)
        for handler in self._handlers:
            handler(*args, **kwargs)

# 使用示例
class Button:
    def __init__(self):
        self.click = Event()  # 定义点击事件
    
    def press(self):
        # 按钮被按下时触发点击事件
        print("按钮被按下")
        self.click.fire()

# 创建按钮实例
button = Button()

# 定义事件处理程序
def on_button_click():
    print("点击事件处理程序被调用")

def on_button_click2():
    print("第二个点击事件处理程序被调用")

# 注册事件处理程序
button.click.add_handler(on_button_click)
button.click.add_handler(on_button_click2)

# 触发事件
button.press()
# 输出:
# 按钮被按下
# 点击事件处理程序被调用
# 第二个点击事件处理程序被调用

# 移除一个处理程序
button.click.remove_handler(on_button_click2)
button.press()
# 输出:
# 按钮被按下
# 点击事件处理程序被调用

3. 使用第三方库

Python中有一些第三方库专门提供了事件系统的实现,如 pydispatch 或 blinker :

使用blinker库实现事件:

from blinker import signal

# 创建一个信号(类似事件)
button_clicked = signal('button_clicked')

# 定义事件处理函数
@button_clicked.connect
def on_button_clicked(sender):
    print(f"按钮 {sender} 被点击")

# 发送信号(触发事件)
button_clicked.send('登录按钮')
# 输出: 按钮 登录按钮 被点击

4. Python实现与C#的主要区别

- 类型安全 :C#的委托是类型安全的,而Python的函数传递是动态类型的

- 语法简洁性 :Python没有专门的委托和事件关键字,但实现方式更加灵活

- 多播委托 :C#的委托支持多播(一个委托可以引用多个方法),Python需要手动管理多个处理函数

- 线程安全 :C#的事件在多线程环境下是线程安全的,Python的自定义事件系统需要自己处理线程安全问题


5. 实际应用场景

Python中的这些机制常用于:

- GUI编程中的事件处理(如Tkinter、PyQt中的事件系统)

- 异步编程中的回调函数

- 插件系统的实现

- 观察者模式的应用

Python虽然没有C#那样的事件和委托关键字,但通过其灵活的函数特性和面向对象设计,可以实现类似甚至更强大的功能。



(五)访问控制

Python的访问控制与其他语言(如C#、Java)有很大不同,它主要依赖于命名约定而非严格的访问修饰符。

下面详细解释Python中与下划线前缀相关的访问控制机制:


1. 单下划线前缀(_var)

单下划线前缀是一种约定,表示该属性或方法被视为"内部使用",不应该被外部直接访问。

这只是一种程序员之间的约定,Python解释器不会强制执行。

特点:

- 表示"私有"的建议,无语法限制

- 导入模块时,使用 from module import * 不会导入以单下划线开头的名称

- 不会触发名称修饰(name mangling)

示例:

class MyClass:
    def __init__(self):
        self.public_var = "可以自由访问"
        self._private_var = "建议内部使用"
    
    def _internal_method(self):
        return "这是一个内部方法"

# 使用
obj = MyClass()
print(obj.public_var)  # 正常访问
print(obj._private_var)  # 技术上可以访问,但不建议
obj._internal_method()  # 技术上可以调用,但不建议

注意:上面例子中,外部访问是不加划线,内部访问的是加_。

public和private这两上拼写并不能决定这个。

如果public_var改为_public_var那它就是内部访问了。

这体现了Python"约定优于配置"的设计哲学。


2. 双下划线前缀(__var)

双下划线前缀会触发Python的名称修饰机制(name mangling),这是Python中最接近"私有"的概念。

特点:

- 会被Python解释器自动修改名称为 _ClassName__var

- 主要目的是避免子类意外覆盖父类的方法或属性

- 提供了一定程度的访问保护,但仍可通过修改后的名称访问

示例:

class MyClass:
    def __init__(self):
        self.__private_var = "这是双下划线私有变量"
    
    def __private_method(self):
        return "这是双下划线私有方法"
    
    def access_private(self):
        # 类内部可以正常访问
        return self.__private_var + " " + self.__private_method()

# 使用
obj = MyClass()
# print(obj.__private_var)  # 报错: AttributeError
# obj.__private_method()  # 报错: AttributeError

# 通过修改后的名称访问
print(obj._MyClass__private_var)  # 输出: 这是双下划线私有变量
print(obj.access_private())  # 通过公共方法访问私有成员


3. 双下划线前后缀(__var__)

这种格式用于Python的特殊方法(魔术方法),如 __init__ 、 __str__ 等,通常有特殊用途。

特点:

- 由Python保留用于特殊用途

- 自定义名称应避免使用这种格式


4. 名称修饰(Name Mangling)原理

当类中定义以双下划线开头的属性或方法时,Python解释器会自动将其名称修改为 _ClassName__var ,

这可以防止子类意外覆盖父类的方法或属性。

示例:

class Parent:
    def __method(self):
        return "父类方法"
    
    def call_method(self):
        return self.__method()

class Child(Parent):
    def __method(self):  # 不会覆盖父类的__method
        return "子类方法"

parent = Parent()
child = Child()
print(parent.call_method())  # 输出: 父类方法
print(child.call_method())   # 输出: 父类方法(而不是子类方法)

5. 实际应用建议

- 对于不希望被外部直接访问的成员,使用单下划线前缀

- 当需要防止子类覆盖父类方法或属性时,使用双下划线前缀

- 遵循"我们都是成年人"的原则,Python更多依赖程序员的自律

- 在API设计中,明确区分公共接口(无下划线)和内部实现(单下划线)

Python的访问控制机制体现了其"约定优于配置"的设计哲学,它给予开发者很大的灵活性,

同时也要求开发者遵守这些约定以保持代码的可维护性。


(四)多进程编程

Python完全支持多进程编程,这是Python中实现并行计算的重要方式,特别适合处理CPU密集型任务。

下面详细介绍Python多进程编程的相关内容:


1. 多进程的实现模块

Python主要通过 multiprocessing 模块实现多进程编程,该模块提供了类似于 threading 的API,

但创建的是独立的进程而不是线程。

2. 基本使用示例

import multiprocessing
import time

def worker(num):
    """进程工作函数"""
    print(f'进程 {num} 开始工作')
    time.sleep(2)
    print(f'进程 {num} 完成工作')

if __name__ == '__main__':
    # 创建并启动3个进程
    processes = []
    for i in range(3):
        p = multiprocessing.Process(target=worker, args=(i,))
        processes.append(p)
        p.start()
    
    # 等待所有进程结束
    for p in processes:
        p.join()
    
    print('所有进程执行完毕')

3. 多进程的主要特点

- 真正的并行执行 :每个进程有独立的Python解释器和内存空间,不受GIL(全局解释器锁)的限制,可以充分利用多核CPU

- 进程间通信 :由于进程间内存不共享,需要使用特定的通信机制,如队列(Queue)、管道(Pipe)、共享内存(Value/Array)等

- 资源消耗较大 :创建和管理进程比线程开销大,每个进程都有独立的内存空间

- 稳定性高 :一个进程崩溃不会影响其他进程

4. 进程池的使用

对于大量短期任务,可以使用进程池来避免频繁创建和销毁进程的开销:

import multiprocessing

def square(x):
    return x * x

if __name__ == '__main__':
    # 创建包含4个进程的进程池
    with multiprocessing.Pool(processes=4) as pool:
        # 映射任务到进程池
        results = pool.map(square, [1, 2, 3, 4, 5])
        print(f'计算结果: {results}')  # 输出: [1, 4, 9, 16, 25]


5. 进程间通信方式

- 队列(Queue) :线程安全的多生产者、多消费者队列

- 管道(Pipe) :用于两个进程间的通信

- 共享内存(Value/Array) :在进程间共享变量

- 管理器(Manager) :创建可在进程间共享的更复杂的数据结构(列表、字典等)


6. 多进程与多线程的选择

- 多进程 :适合CPU密集型任务,如数据计算、图像处理等

- 多线程 :适合I/O密集型任务,如网络请求、文件读写等


7. 其他相关模块

- concurrent.futures.ProcessPoolExecutor :提供高级异步执行接口

- subprocess :用于创建新进程并与之交互

- os.fork() :在Unix/Linux系统上创建进程(Windows不支持)

Python的多进程编程为CPU密集型任务提供了真正的并行计算能力,

有效弥补了多线程受GIL限制的不足,是Python并行编程的重要组成部分。



(三)python的多线程

Python的多线程是一种并发编程方式,但由于GIL(全局解释器锁)的存在,

其特性与其他语言有所不同。下面详细介绍Python多线程的相关内容:


1. 基本概念与使用

Python的多线程通过内置的 threading 模块实现。多线程允许程序同时执行多个任务,

但需要注意的是,由于GIL的限制,Python的多线程在CPU密集型任务中无法真正实现并行。


基本使用示例 :

import threading
import time

# 定义线程函数
def print_numbers():
    for i in range(5):
        print(f"数字: {i}")
        time.sleep(1)

def print_letters():
    for letter in 'ABCDE':
        print(f"字母: {letter}")
        time.sleep(1)

# 创建并启动线程
thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_letters)

thread1.start()
thread2.start()

# 等待线程结束
thread1.join()
thread2.join()

print("所有线程执行完毕")

2. GIL的影响

**GIL(全局解释器锁)**是Python解释器中的一个机制,它确保在任何时刻只有一个线程在执行Python字节码。

这意味着,即使在多核CPU上,Python的多线程也无法同时执行Python代码。

GIL的影响 :

- 对于CPU密集型任务(如大量计算),多线程并不能提高性能,甚至可能因为线程切换而降低性能

- 对于I/O密集型任务(如网络请求、文件读写),多线程可以提高效率,因为线程在等待I/O时会释放GIL,允许其他线程执行


3. 线程同步

由于多线程共享同一进程的内存空间,当多个线程访问共享资源时,需要进行同步以避免数据竞争。

Python提供了多种线程同步机制:

锁(Lock) :

import threading

# 创建锁对象
lock = threading.Lock()
shared_counter = 0

def increment_counter():
    global shared_counter
    for _ in range(1000):
        with lock:  # 使用with语句自动获取和释放锁
            shared_counter += 1

# 创建多个线程
threads = []
for _ in range(10):
    thread = threading.Thread(target=increment_counter)
    threads.append(thread)
    thread.start()

# 等待所有线程完成
for thread in threads:
    thread.join()

print(f"最终计数器值: {shared_counter}")  # 应该是10000

其他同步原语 :

- RLock :可重入锁,允许同一线程多次获取锁

- Condition :条件变量,用于线程间的通知机制

- Semaphore :信号量,控制对资源的访问数量

- Event :事件,用于线程间的简单通知

- Barrier :栅栏,等待指定数量的线程到达后再继续


4. 守护线程

守护线程(Daemon Thread)是一种在后台运行的线程,当所有非守护线程结束时,守护线程会被自动终止。

import threading
import time

def background_task():
    while True:
        print("守护线程运行中...")
        time.sleep(1)

# 创建守护线程
daemon_thread = threading.Thread(target=background_task)
daemon_thread.daemon = True  # 设置为守护线程
daemon_thread.start()

# 主线程执行一段时间后结束
print("主线程开始执行")
time.sleep(3)
print("主线程结束,守护线程也会被终止")


5. 线程池

对于需要创建大量线程的场景,使用线程池可以更高效地管理线程资源。

Python的 concurrent.futures 模块提供了 ThreadPoolExecutor 类来实现线程池。

import concurrent.futures
import time

def process_task(task_id):
    print(f"开始处理任务 {task_id}")
    time.sleep(2)  # 模拟耗时操作
    return f"任务 {task_id} 完成"

# 创建线程池
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    # 提交任务到线程池
    future_to_task = {executor.submit(process_task, i): i for i in range(5)}
    
    # 获取任务结果
    for future in concurrent.futures.as_completed(future_to_task):
        task_id = future_to_task[future]
        try:
            result = future.result()
            print(result)
        except Exception as e:
            print(f"任务 {task_id} 发生异常: {e}")


6. Python多线程的适用场景

由于GIL的存在,Python多线程主要适用于以下场景:

- I/O密集型任务(网络请求、文件读写、数据库操作等)

- 有大量等待时间的任务

- 需要响应式UI的应用程序

对于CPU密集型任务,Python提供了 multiprocessing 模块来实现真正的并行计算,它通过创建多个进程来绕过GIL的限制。


7. 与多进程的对比

image.png

8. 并发编程的其他选择

除了多线程和多进程,Python还提供了异步编程的方式来处理并发:

异步编程(async/await) :

import asyncio

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    # 并发执行两个协程
    task1 = asyncio.create_task(say_after(1, "你好"))
    task2 = asyncio.create_task(say_after(2, "世界"))
    
    print("开始")
    
    # 等待两个任务完成
    await task1
    await task2
    
    print("结束")

# 运行主协程
asyncio.run(main())

异步编程适用于I/O密集型任务,它使用单线程事件循环来管理多个任务,避免了线程切换的开销。


总结

Python的多线程是一种并发编程方式,由于GIL的存在,它在CPU密集型任务中无法实现真正的并行,

但在I/O密集型任务中可以提高效率。

在实际开发中,应根据任务类型选择合适的并发方式:

I/O密集型任务可以使用多线程或异步编程,而CPU密集型任务则应使用多进程。



(二)动态语言的重要概念:鸭子类型

鸭子类型(Duck Typing)是Python等动态类型语言中的一个重要概念,

它强调对象的行为(方法和属性)比对象的具体类型更重要。

这个概念源自一句谚语:"如果它走路像鸭子,叫声像鸭子,那么它就是鸭子"。


核心思想

在鸭子类型系统中,我们不关心一个对象是什么类型,只关心它是否具有我们需要的方法或属性。

只要对象实现了特定的方法或具有特定的属性,我们就可以用它来完成相应的任务,

而不需要它属于某个特定的类或实现某个特定的接口。


简单示例

假设我们有一个函数,它需要一个可以"叫"的对象:

def make_sound(animal):
    animal.speak()

现在,我们有两个完全不同的类:

class Dog:
    def speak(self):
        print("汪汪汪!")

class Cat:
    def speak(self):
        print("喵喵喵!")

尽管 Dog 和 Cat 是两个完全不同的类,它们之间没有继承关系,但它们都有一个 speak() 方法。

因此,我们可以将它们的实例都传递给 make_sound() 函数:

dog = Dog()
cat = Cat()

make_sound(dog)  # 输出: 汪汪汪!
make_sound(cat)  # 输出: 喵喵喵!

这里, make_sound() 函数并不关心传入的对象是什么类型,只要它有一个 speak() 方法即可,这就是鸭子类型的核心思想。


更实际的例子

在Python中,很多内置函数和操作符都依赖于鸭子类型。

例如, len() 函数不关心对象是什么类型,只要它有 __len__() 方法即可:

class Book:
    def __init__(self, title, pages):
        self.title = title
        self.pages = pages
    
    def __len__(self):
        return self.pages

class Library:
    def __init__(self, books):
        self.books = books
    
    def __len__(self):
        return len(self.books)

# 创建对象
book = Book("Python编程", 300)
library = Library([book, Book("数据分析", 400)])

# 使用len()函数
print(len(book))      # 输出: 300
print(len(library))   # 输出: 2


同样,迭代器协议也是基于鸭子类型的。

一个对象只要实现了 __iter__() 和 __next__() 方法,就可以被用于 for 循环中。


与接口的区别

在静态类型语言如C#中,我们通常需要定义接口并让类显式实现这些接口。例如:

// C#中的接口定义
interface ISpeaker
{
    void Speak();
}

// 显式实现接口
class Dog : ISpeaker
{
    public void Speak()
    {
        Console.WriteLine("汪汪汪!");
    }
}

// 函数接受接口类型
void MakeSound(ISpeaker speaker)
{
    speaker.Speak();
}

而在Python的鸭子类型系统中,我们不需要显式定义接口或让类显式实现接口,只要对象具有所需的方法即可。

这种方式更加灵活,但也可能在运行时才发现错误(例如,如果对象没有所需的方法)。


鸭子类型的优缺点

优点 :

- 代码更加灵活,可以轻松扩展

- 不需要复杂的继承层次结构

- 促进了组合优于继承的设计原则

缺点 :

- 错误可能在运行时才被发现

- 代码的可读性可能降低,因为类型信息不明确

- IDE的自动完成和类型检查功能可能不够强大


为了弥补这些缺点,Python 3引入了类型提示(Type Hints),允许开发者在代码中添加类型信息,

虽然这些信息在运行时会被忽略,但可以提高代码的可读性和可维护性,也有助于IDE提供更好的支持。


总之,鸭子类型是Python灵活性的重要体现,它允许我们编写更加通用和可复用的代码,

而不必严格遵守特定的类型层次结构。




(一)python与C#的不同点

Python和C#是两种流行但设计理念差异较大的编程语言,以下是它们之间的主要不同点:


1. 语法特点

Python :

- 使用缩进(空格或制表符)来表示代码块,没有大括号

- 语句结尾不需要分号

- 动态类型,变量声明不需要指定类型

- 简洁的语法,代码行数通常更少

- 支持多行字符串和文档字符串

C# :

- 使用大括号 {} 表示代码块

- 语句结尾需要分号

- 静态类型,变量声明必须指定类型(C# 10后支持var类型推断)

- 更严格的语法规则

- 支持XML文档注释

示例对比 :


# Python定义函数

def greet(name):

    if name:

        return f"Hello, {name}!"

    else:

        return "Hello, World!"


// C#定义函数

public string Greet(string name)

{

    if (name != null)

    {

        return $"Hello, {name}!";

    }

    else

    {

        return "Hello, World!";

    }

}


2. 类型系统

Python :

- 动态类型语言,变量类型在运行时确定

- 弱类型系统,可以进行隐式类型转换

- 变量不需要声明类型

- 支持鸭子类型("如果它走路像鸭子,叫声像鸭子,那么它就是鸭子")

C# :

- 静态类型语言,变量类型在编译时确定

- 强类型系统,类型转换需要显式进行

- 变量必须声明类型

- 主要基于名义类型系统


3. 执行方式

Python :

- 解释型语言,代码逐行解释执行

- 不需要编译成中间代码(尽管CPython有字节码)

- 执行速度相对较慢

- 跨平台通过解释器实现

C# :

- 编译型语言,代码先编译为中间语言(IL)

- 运行时通过.NET CLR(公共语言运行时)的JIT编译器转换为机器码

- 执行速度较快

- 跨平台通过.NET Core/.NET 5+实现


4. 内存管理

Python :

- 使用引用计数作为主要的垃圾回收机制

- 辅以循环引用检测器解决引用计数无法处理的情况

- 自动内存管理,无需手动释放内存

- 有全局解释器锁(GIL)限制多线程性能

C# :

- 使用标记-清除算法的垃圾回收器

- 有分代垃圾回收机制

- 自动内存管理,支持析构函数和using语句处理资源

- 无GIL,多线程可以充分利用多核


5. 编程范式

Python :

- 支持多种编程范式:面向对象、函数式、命令式

- 对函数式编程支持较好(高阶函数、lambda表达式等)

- 设计哲学是"一种明确的做事方式"(虽然实际中可能有多种方式)

- 强调代码可读性("可读性计数")

C# :

- 主要是面向对象编程语言

- 近年来增加了对函数式编程的支持(LINQ、lambda表达式等)

- 设计哲学更强调类型安全和性能

- 支持更多的OOP特性(如访问修饰符、接口、抽象类等)


6. 生态系统

Python :

- 强大的科学计算和数据分析库(NumPy、Pandas、SciPy)

- 优秀的机器学习框架(TensorFlow、PyTorch、scikit-learn)

- 丰富的Web框架(Django、Flask、FastAPI)

- 广泛应用于数据科学、人工智能、自动化脚本等领域

C# :

- 强大的企业应用开发框架(ASP.NET、Entity Framework)

- 优秀的桌面应用开发工具(WPF、WinForms、MAUI)

- 游戏开发支持(Unity游戏引擎使用C#)

- 广泛应用于企业软件、Web应用、游戏开发等领域


7. 异常处理

Python :

- 使用try-except-else-finally结构

- 异常是类,所有异常继承自BaseException

- 支持else块(当没有异常时执行)

- 采用EAFP(Easier to Ask Forgiveness than Permission)风格

C# :

- 使用try-catch-finally结构

- 异常是类,所有异常继承自Exception

- 没有else块

- 支持异常过滤器

- 更倾向于LBYL(Look Before You Leap)风格


8. 并发与并行

Python :

- 由于GIL的存在,多线程在CPU密集型任务中无法真正并行

- 对于CPU密集型任务,通常使用多进程(multiprocessing模块)

- 对异步编程支持良好(async/await语法)

C# :

- 没有GIL限制,多线程可以真正并行执行

- 提供丰富的并发编程API(Task、Parallel、async/await等)

- 对异步编程支持完善


9. 访问控制

Python :

- 没有严格的访问控制机制

- 使用约定来表示私有成员(单下划线和双下划线前缀)

- 双下划线前缀会触发名称修饰,但仍可访问

C# :

- 有严格的访问控制机制

- 提供public、private、protected、internal等访问修饰符

- 严格限制对私有成员的访问


10. 扩展性

Python :

- 可以轻松集成C/C++代码以提高性能

- 使用ctypes、Cython等工具与C/C++交互

- 本身是解释型语言,扩展模块通常编译为共享库

C# :

- 可以使用P/Invoke调用本机代码

- 支持通过COM互操作与其他语言交互

- 主要是编译型语言,扩展通常通过类库实现


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:
本帖最后由 勇哥,很想停止 于 2025-10-08 16:50:52 编辑

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2025年10月    »
12345
6789101112
13141516171819
20212223242526
2728293031
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864