python进阶-自学笔记

面向对象

多态

在Python中,多态(Polymorphism)是面向对象编程中的一个重要概念,它允许不同类的对象以相同的接口进行操作。多态使得代码更加灵活和可扩展,因为函数或方法可以处理不同类型的对象,而无需知道它们的具体类。

1. 多态的基本概念

多态可以通过以下两种主要方式实现:

  • 重写方法:子类重写父类的方法,以实现不同的行为。
  • 接口多态:不同类实现相同的方法,使得它们可以通过相同的接口进行操作。

2. 示例

方法重写
class Animal:
    def speak(self):
        return "Some sound"

class Dog(Animal):
    def speak(self):
        return "Bark"

class Cat(Animal):
    def speak(self):
        return "Meow"

def animal_sound(animal):
    print(animal.speak())

dog = Dog()
cat = Cat()

animal_sound(dog)  # 输出:Bark
animal_sound(cat)  # 输出:Meow

在这个例子中,DogCat 类分别重写了 Animal 类的 speak 方法。animal_sound 函数能够处理任何 Animal 类型的对象,并调用其 speak 方法。

接口多态
class Bird:
    def fly(self):
        return "Flying high"

class Airplane:
    def fly(self):
        return "Flying with engines"

def test_fly(obj):
    print(obj.fly())

bird = Bird()
airplane = Airplane()

test_fly(bird)  # 输出:Flying high
test_fly(airplane)  # 输出:Flying with engines

在这个例子中,BirdAirplane 类都有一个 fly 方法,test_fly 函数能够处理任何有 fly 方法的对象。

3. 多态的优势

  • 灵活性:可以编写更加灵活和可扩展的代码,因为可以对不同对象进行相同的操作。
  • 可维护性:通过多态,可以将类的实现细节封装在内部,减少对外部代码的影响。
  • 代码复用:相同的接口可以被多个类实现,从而提高代码的复用性。

4. 多态在标准库中的应用

Python标准库中许多内置函数和方法都是多态的。例如,len() 函数可以作用于字符串、列表、字典等不同类型的对象,只要它们实现了 __len__() 方法。

print(len("hello"))  # 输出:5
print(len([1, 2, 3]))  # 输出:3
print(len({"a": 1, "b": 2}))  # 输出:2

5. 鸭子类型

Python采用鸭子类型(Duck Typing)的动态多态性,这意味着对象的实际类型并不重要,只要它有需要的方法或属性即可。

class Duck:
    def quack(self):
        return "Quack"

class Person:
    def quack(self):
        return "I'm quacking like a duck!"

def make_quack(obj):
    print(obj.quack())

duck = Duck()
person = Person()

make_quack(duck)  # 输出:Quack
make_quack(person)  # 输出:I'm quacking like a duck!

在这个例子中,无论是 Duck 还是 Person,只要它们实现了 quack 方法,就可以被 make_quack 函数处理。

类属性和实例属性

在Python中,类属性和实例属性是定义类时的重要组成部分。它们分别用于描述类本身的属性和类实例的属性。

1. 类属性

  • 定义:类属性是在类定义中直接声明的属性,属于类本身。
  • 共享性:所有实例共享同一个类属性。
  • 访问方式:可以通过类名或实例名访问。
示例
class MyClass:
    class_attribute = "I am a class attribute"

# 访问类属性
print(MyClass.class_attribute)  # 输出:I am a class attribute

# 创建实例
instance1 = MyClass()
instance2 = MyClass()

# 通过实例访问类属性
print(instance1.class_attribute)  # 输出:I am a class attribute
print(instance2.class_attribute)  # 输出:I am a class attribute

# 修改类属性
MyClass.class_attribute = "Changed class attribute"
print(instance1.class_attribute)  # 输出:Changed class attribute
print(instance2.class_attribute)  # 输出:Changed class attribute

2. 实例属性

  • 定义:实例属性是在类的 __init__ 方法中使用 self 关键字定义的,属于类的每个实例。
  • 独立性:每个实例都有自己独立的实例属性。
  • 访问方式:只能通过实例名访问。
示例
class MyClass:
    def __init__(self, value):
        self.instance_attribute = value

# 创建实例
instance1 = MyClass("Instance 1 attribute")
instance2 = MyClass("Instance 2 attribute")

# 访问实例属性
print(instance1.instance_attribute)  # 输出:Instance 1 attribute
print(instance2.instance_attribute)  # 输出:Instance 2 attribute

# 修改实例属性
instance1.instance_attribute = "Modified instance 1 attribute"
print(instance1.instance_attribute)  # 输出:Modified instance 1 attribute
print(instance2.instance_attribute)  # 输出:Instance 2 attribute

3. 类属性与实例属性的区别

  • 范围:类属性是类级别的,所有实例共享;实例属性是实例级别的,每个实例独立。
  • 修改影响:修改类属性会影响所有实例;修改实例属性只影响特定实例。
  • 定义位置:类属性在类体内定义,通常在方法外;实例属性在 __init__ 方法中定义,使用 self 关键字。

4. 使用场景

  • 类属性:适合存储与类相关的共享数据,例如常量或配置信息。
  • 实例属性:适合存储与实例相关的独立数据,例如对象的状态或特征。

5. 注意事项

在 Python 中,如果使用实例对象对类属性赋值,会发生以下情况:

  1. 实例属性覆盖: 如果实例对象对一个类属性赋值,Python 会在该实例的命名空间中创建一个新的实例属性,而不会改变类属性。此时,实例属性会覆盖同名的类属性。

  2. 影响范围: 这种操作只会影响当前实例,不会影响类本身或其他实例。

示例:

class MyClass:
    class_attribute = 10

# 创建实例
obj = MyClass()

# 通过实例修改类属性(实际上创建了实例属性)
obj.class_attribute = 20

print(MyClass.class_attribute)  # 输出: 10
print(obj.class_attribute)      # 输出: 20

在这个示例中,MyClass.class_attribute 保持不变,而 obj.class_attribute 是实例自己的属性,覆盖了类属性的值。

类方法和静态方法

在Python中,类方法和静态方法是定义在类中的两种特殊方法。它们的定义和使用方式不同于实例方法。

1. 类方法(Class Method)

  • 定义:使用 @classmethod 装饰器定义。第一个参数通常为 cls,表示类本身。
  • 用途:类方法可以访问和修改类属性,适合用于操作类级别的数据。
示例
class MyClass:
    class_attribute = 0

    @classmethod
    def increment_class_attribute(cls):
        cls.class_attribute += 1

# 调用类方法
MyClass.increment_class_attribute()
print(MyClass.class_attribute)  # 输出:1

# 创建实例
instance = MyClass()
instance.increment_class_attribute()
print(MyClass.class_attribute)  # 输出:2

2. 静态方法(Static Method)

  • 定义:使用 @staticmethod 装饰器定义。静态方法不需要表示实例或类的特殊参数。
  • 用途:静态方法与类和实例无关,适合执行一些逻辑上与类有关但不需要访问类或实例的数据。
示例
class MathUtils:
    @staticmethod
    def add(x, y):
        return x + y

# 调用静态方法
result = MathUtils.add(3, 5)
print(result)  # 输出:8

# 静态方法可以通过类名或实例调用
instance = MathUtils()
print(instance.add(10, 20))  # 输出:30

3. 类方法与静态方法的区别

  • 参数:类方法需要 cls 参数来表示类本身;静态方法没有默认参数。
  • 访问权限:类方法可以访问和修改类属性;静态方法不能直接访问类或实例属性。
  • 使用场景:类方法用于需要类级别上下文的场合;静态方法用于独立于类或实例的逻辑。

4. 实际应用

  • 类方法:常用于实现工厂方法,提供一些与类相关的辅助功能。
  • 静态方法:常用于实现工具函数,不依赖类或实例的数据。

私有化属性和私有化方法

在Python中,私有化属性和私有化方法用于限制对类内部成员的访问,从而保护数据和实现的封装。以下是详细介绍:

私有化属性

  • 定义:通过在属性名前加下划线 _ 或双下划线 __ 来实现。
    • 单下划线 _: 表示属性是“受保护的”(非正式的约定,不建议外部直接访问)。
    • 双下划线 __: 表示属性是私有的(名字会被改写为 _ClassName__attribute,避免外部直接访问)。
示例
class MyClass:
    def __init__(self):
        self._protected_attribute = "protected"
        self.__private_attribute = "private"

    def get_private_attribute(self):
        return self.__private_attribute

# 实例化对象
obj = MyClass()

# 访问受保护属性(建议仅内部访问)
print(obj._protected_attribute)  # 输出:protected

# 访问私有属性(通过类内部方法)
print(obj.get_private_attribute())  # 输出:private

# 直接访问私有属性(不建议)
# print(obj.__private_attribute)  # 会报错

# 访问私有属性的变通方法(不推荐)
print(obj._MyClass__private_attribute)  # 输出:private

私有化方法

  • 定义:通过在方法名前加下划线 _ 或双下划线 __ 来实现。
    • 单下划线 _: 表示方法是“受保护的”。
    • 双下划线 __: 表示方法是私有的。
示例
class MyClass:
    def __init__(self):
        pass

    def _protected_method(self):
        return "This is a protected method."

    def __private_method(self):
        return "This is a private method."

    def call_private_method(self):
        return self.__private_method()

# 实例化对象
obj = MyClass()

# 访问受保护方法(建议仅内部访问)
print(obj._protected_method())  # 输出:This is a protected method.

# 访问私有方法(通过类内部方法)
print(obj.call_private_method())  # 输出:This is a private method.

# 直接访问私有方法(不建议)
# print(obj.__private_method())  # 会报错

# 访问私有方法的变通方法(不推荐)
print(obj._MyClass__private_method())  # 输出:This is a private method.

总结

  • 单下划线 _: 用于标记属性或方法为受保护的,仅供类或子类内部使用。外部访问是可能的,但不建议。
  • 双下划线 __: 用于标记属性或方法为私有的,避免外部访问。通过名称改写机制,可以在类内部访问,但外部访问需通过变通方式(不推荐)。
  • 访问方式: 私有化是为了提高数据的封装性,建议通过类内部的方法进行访问。

Property 属性函数

在Python中,property 是一个内置函数,用于创建和管理属性的访问控制。它可以将类的方法转换为只读属性,并实现属性的封装,使得对属性的访问既可以保持简单,又可以在幕后添加自定义逻辑。

1. 基本用法

property 函数可以用来定义一个属性,通过它可以控制属性的获取、设置和删除操作。

语法
property(fget=None, fset=None, fdel=None, doc=None)
  • fget:获取属性值的函数。
  • fset:设置属性值的函数。
  • fdel:删除属性值的函数。
  • doc:属性的文档字符串。

2. 示例

使用 property 创建只读属性
class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

circle = Circle(5)
print(circle.radius)  # 输出:5
# circle.radius = 10  # 会报错,因为没有定义 setter
使用 property 实现属性的 getter 和 setter
class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value < 0:
            raise ValueError("Radius cannot be negative")
        self._radius = value

circle = Circle(5)
print(circle.radius)  # 输出:5
circle.radius = 10
print(circle.radius)  # 输出:10
# circle.radius = -1  # 会报错:ValueError: Radius cannot be negative
使用 property 实现属性的删除器
class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value < 0:
            raise ValueError("Radius cannot be negative")
        self._radius = value

    @radius.deleter
    def radius(self):
        del self._radius

circle = Circle(5)
print(circle.radius)  # 输出:5
del circle.radius
# print(circle.radius)  # 会报错:AttributeError: 'Circle' object has no attribute '_radius'

3. property 的优势

  • 封装性:通过 property,可以在属性访问时添加逻辑,比如验证或转换数据。
  • 只读属性:可以轻松创建只读属性,防止属性被外部修改。
  • 接口统一:外部使用者无需知道属性是直接存取还是通过方法访问,提供了统一的接口。

单例模式和 __new__ 方法

__new__ 方法

__new__ 是一个特殊方法,用于控制对象的创建。它在 __init__ 方法之前被调用,负责返回类的一个实例。通常你不需要重写这个方法,但在实现某些设计模式(如单例模式)时,它可能会被重写。

  • 用法__new__ 接收类自身作为第一个参数,通常用 cls 表示。返回值必须是该类的一个实例。

单例模式

单例模式确保一个类只有一个实例,并提供一个全局访问点。单例模式通常在需要共享资源(如数据库连接、日志记录器)时使用。

实现方法

单例模式可以通过多种方式实现,最常见的方式是重写 __new__ 方法。示例如下:

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._instance

    def __init__(self):
        pass

# 测试单例
obj1 = Singleton()
obj2 = Singleton()

print(obj1 is obj2)  # 输出:True

在这个示例中,__new__ 方法被重写以确保 Singleton 类只能有一个实例。_instance 类变量用于存储这个实例。每次创建对象时,都会检查 _instance 是否已经存在,如果不存在,则调用 super() 创建一个新实例,否则返回现有实例。

总结

  • __new__ 用于控制对象的创建过程。
  • 单例模式通过 __new__ 方法可以保证一个类只有一个实例。

错误与异常处理

1. 异常的基本概念

  • 异常:在程序运行时发生的错误。Python 使用异常对象来表示错误情况。
  • 捕获异常:通过 tryexcept 块捕获和处理异常,以防止程序崩溃。

2. 常见异常类型

  • ValueError:传递给函数的参数类型正确但值不合适。
  • TypeError:操作或函数应用于不适当类型的对象。
  • IndexError:序列索引超出范围。
  • KeyError:字典中访问不存在的键。
  • ZeroDivisionError:除数为零。

3. 异常处理机制

基本语法
try:
    # 可能引发异常的代码
    pass
except ExceptionType:
    # 处理异常的代码
    pass
else:
    # 如果没有异常发生,执行的代码
    pass
finally:
    # 无论是否发生异常,都会执行的代码
    pass
示例
try:
    result = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero")
else:
    print("Division successful")
finally:
    print("Execution complete")

4. 多个异常处理

可以在 except 语句中处理多个异常类型。

try:
    value = int("abc")
except ValueError:
    print("ValueError occurred")
except TypeError:
    print("TypeError occurred")

5. 捕获所有异常

使用 Exception 类可以捕获所有异常,但应谨慎使用,避免掩盖真实错误。

try:
    value = int("abc")
except Exception as e:
    print(f"An error occurred: {e}")

6. 自定义异常

可以定义自己的异常类,继承自 Exception

class MyCustomError(Exception):
    pass

def check_value(value):
    if value < 0:
        raise MyCustomError("Value cannot be negative")

try:
    check_value(-1)
except MyCustomError as e:
    print(e)

7. 使用 assert 语句

assert 用于检查条件是否为真,若为假则抛出 AssertionError

x = 5
assert x > 0, "x must be positive"

8. raise 语句

在Python中,raise 用于显式引发异常。它可以用于中断程序的正常流程,并将控制权交给异常处理器。

8.1 基本用法

raise 用于抛出一个指定的异常。

raise Exception("An error occurred")
8.2 常见用法
抛出内置异常
def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("Cannot divide by zero")
    return a / b

try:
    divide(10, 0)
except ZeroDivisionError as e:
    print(e)
抛出自定义异常
class CustomError(Exception):
    pass

def check_value(value):
    if value < 0:
        raise CustomError("Value cannot be negative")

try:
    check_value(-1)
except CustomError as e:
    print(e)
8.3 重新抛出异常

在异常处理器中,可以使用 raise 不带参数重新抛出当前异常,以便进一步处理。

try:
    raise ValueError("An error occurred")
except ValueError as e:
    print("Caught ValueError")
    raise  # 重新抛出异常

动态绑定属性和方法

动态绑定属性(实例和类对象均可)

添加属性

可以直接为对象添加新属性

class MyClass:
    pass

obj = MyClass()
obj.new_attribute = "Hello"
print(obj.new_attribute)  # 输出:Hello
修改属性

可以直接修改对象已有的属性

obj.new_attribute = "World"
print(obj.new_attribute)  # 输出:World

动态绑定方法

添加方法

可以将函数动态绑定到对象作为方法。

def greet(self):
    return "Hello!"

obj.greet = greet.__get__(obj)
print(obj.greet())  # 输出:Hello!
使用 types.MethodType

可以使用 types.MethodType 将函数绑定为实例方法。

from types import MethodType

# 有"self"代表绑定的是实例方法
def greet(self): # 注意这里的"self",如果没有"self"可以直接obj.greet = greet
    return "Hello!"

obj.greet = MethodType(greet, obj)
print(obj.greet())  # 输出:Hello!

# 绑定类方法:
@classmethod
def test(cls):
    pass

MyClass.test = test

_slots__ 属性

在Python中,__slots__ 属性用于限制类实例能动态添加的属性,从而提高内存效率和访问速度。

1. 基本概念

  • 限制属性:使用 __slots__ 定义的类只能拥有特定的属性,无法动态添加其他属性。
  • 内存优化:避免为每个实例创建 __dict__,减少内存消耗。

2. 使用方法

定义 __slots__
class MyClass:
    __slots__ = ['name', 'age']

    def __init__(self, name, age):
        self.name = name
        self.age = age

# 实例化对象
obj = MyClass("Alice", 30)
print(obj.name)  # 输出:Alice
print(obj.age)   # 输出:30
限制属性
# 尝试添加未定义的属性会抛出 AttributeError
# obj.address = "123 Main St"  # 会抛出 AttributeError

3. 优势

  • 内存使用减少:节省内存,特别是在大量实例化时。
  • 访问速度提升:属性访问速度更快。

4. 注意事项

  • 继承:子类不会继承父类的 __slots__ 定义。
  • 兼容性__slots__ 定义的类不支持某些动态特性,如为实例添加方法。

文件操作

基本操作

打开文件

使用 open() 函数打开文件,语法如下:

file = open(filename, mode)
  • filename: 文件名或路径。
  • mode: 文件打开模式,常见模式包括:
    • 'r': 只读模式,文件必须存在。
    • 'w': 只写模式,文件不存在则创建,存在则清空。
    • 'a': 追加模式,文件不存在则创建,存在则在末尾追加内容(文件指针直接跳到末尾,不可读)。
    • 'b': 二进制模式,不能单独使用,与其他模式组合,如 'rb''wb' 等。
    • '+': 更新模式,不能单独使用,与其他模式组合,如 'r+''w+' 等(可读可写,但是文件打开时的操作的操作取决于 + 的前缀, 与其相同)

读取文件

可以使用 read()readline()readlines() 方法读取文件内容:

# 使用 read() 方法读取整个文件
with open('example.txt', 'r') as file:
    content = file.read() # 在't'模式下, read(n)的n是字符个数
    print(content)

# 使用 readline() 方法逐行读取
with open('example.txt', 'r') as file:
    line = file.readline()
    while line:
        print(line, end='')
        line = file.readline()

# 使用 readlines() 方法读取所有行
with open('example.txt', 'r') as file:
    lines = file.readlines()
    for line in lines:
        print(line, end='')

写入文件

可以使用 write()writelines() 方法写入文件:

# 使用 write() 方法写入单行文本
with open('example.txt', 'w') as file:
    file.write('Hello, world!\n')
    # file.write('...') 不关文件,新写的内容总会写在文件指针的位置
    # 重新打开,则会清空原文件,文件指针移到开头

# 使用 writelines() 方法写入多行文本
lines = ['First line.\n', 'Second line.\n', 'Third line.\n'] # 如果没有\n,则是多次写入
with open('example.txt', 'w') as file:
    file.writelines(lines)

追加内容

在文件末尾追加内容:

with open('example.txt', 'a') as file:
    file.write('Appending this line.\n')

关闭文件

使用 close() 方法关闭文件,但通常推荐使用 with 语句,它可以自动关闭文件:

file = open('example.txt', 'r')
# 进行文件操作
file.close()

# 推荐的方式
with open('example.txt', 'r') as file:
    # 进行文件操作
    pass
# 文件在退出 with 语句块时自动关闭

处理二进制文件

处理二进制文件时,需要在模式中加上 'b'

# 读取二进制文件
with open('example.bin', 'rb') as file:
    content = file.read()
    print(content)

# 写入二进制文件
with open('example.bin', 'wb') as file:
    file.write(b'\x00\x01\x02\x03')

文件指针

seek() 方法

seek() 方法用于将文件指针移动到文件中的一个指定位置。语法如下:

file.seek(offset, whence=0)
  • offset: 相对于 whence 的偏移量(可以是正数或负数)。
  • whence: 可选参数,指定起始位置。默认值为 0,表示从文件开头开始计算。它可以取以下值:
    • 0: 从文件开头计算(默认值)
    • 1: 从当前文件指针位置计算。
    • 2: 从文件末尾计算。
    • 只有 0 模式可以在 t 模式下使用,别的只能在 b 模式下使用
tell() 方法

tell() 方法用于返回文件指针在文件中的当前位置。语法如下:

position = file.tell()
示例代码
移动到文件开头
with open('example.txt', 'r') as file:
    # 读取一些内容
    content = file.read(10)
    print('First 10 bytes:', content)
    
    # 移动文件指针到文件开头
    file.seek(0)
    
    # 再次读取相同内容
    content = file.read(10)
    print('First 10 bytes again:', content)
从当前位置移动文件指针
with open('example.txt', 'r') as file:
    # 读取一些内容
    content = file.read(10)
    print('First 10 bytes:', content)
    
    # 从当前位置向后移动5个字节
    file.seek(5, 1)
    
    # 读取接下来的内容
    content = file.read(10)
    print('Next 10 bytes:', content)
从文件末尾移动文件指针
with open('example.txt', 'rb') as file:
    # 移动文件指针到文件末尾
    file.seek(0, 2)
    
    # 获取文件长度
    file_length = file.tell()
    print('File length:', file_length)
    
    # 向前移动10个字节
    file.seek(-10, 2)
    
    # 读取最后的10个字节
    content = file.read(10)
    print('Last 10 bytes:', content)

注意事项

  1. 文本模式与二进制模式seek()tell() 方法在文本模式(如 'r''w')和二进制模式(如 'rb''wb')下均可使用,但在文本模式下,offset 必须为0或通过seek(0,2)进行绝对位置移动。
  2. tell() 方法返回字节位置:无论文件以何种模式打开,tell() 方法返回的位置始终是字节位置。

通过这些示例和解释,可以更好地理解如何在 Python 中使用 seek()tell() 方法移动文件指针并获取其当前位置。

with 关键字

在 Python 中,with 关键字用于简化对资源的管理,特别是对于需要在使用后进行清理或释放的资源,如文件、网络连接、线程锁等。with 关键字引入了一种上下文管理协议,通过它,可以确保资源在使用结束后得到正确释放,无论是否发生异常。

基本用法

with 关键字通常与上下文管理器一起使用。上下文管理器是一个实现了 __enter____exit__ 方法的对象。__enter__ 方法在进入上下文时执行,__exit__ 方法在离开上下文时执行。

以下是使用 with 关键字打开和处理文件的示例:

# 使用 with 关键字打开文件
with open('example.txt', 'r') as file:
    content = file.read()
    print(content)
# 文件在离开 with 语句块时自动关闭

在上面的示例中,open() 函数返回一个文件对象,该对象实现了上下文管理协议。with 关键字确保无论是否发生异常,文件都会在离开 with 语句块时自动关闭。

上下文管理协议

上下文管理器类通常实现两个方法:__enter____exit__

  • __enter__(self): 进入上下文管理器时执行的代码。通常用于资源的初始化。返回的值(如果有)会赋给 with 语句中的变量。
  • __exit__(self, exc_type, exc_val, exc_tb): 离开上下文管理器时执行的代码。通常用于资源的清理。参数用于处理异常。

示例:自定义上下文管理器

class MyContextManager:
    def __enter__(self):
        print("Entering the context")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting the context")
        if exc_type is not None:
            print(f"An exception occurred: {exc_value}")
        return False  # 返回 True 表示异常已经处理,不会再传播

# 使用自定义上下文管理器
with MyContextManager() as manager:
    print("Inside the context")
    # 模拟异常
    # raise ValueError("An error occurred")

with 关键字的好处

  1. 简洁和清晰: 使用 with 关键字可以使代码更简洁和可读,明确表示资源的管理范围。
  2. 自动资源管理: with 关键字确保资源在使用结束后自动释放,减少了忘记释放资源导致的错误。
  3. 异常处理: 上下文管理器可以处理代码块中的异常,进行相应的清理工作。

多个上下文管理器

Python 3.1 及以上版本支持在一个 with 语句中同时管理多个上下文管理器:

with open('file1.txt', 'r') as file1, open('file2.txt', 'w') as file2:
    content = file1.read()
    file2.write(content)

在这个示例中,file1file2 都会在离开 with 语句块时自动关闭。

例子:使用 with 管理文件操作

def read_file(file_path):
    with open(file_path, 'r') as file:
        return file.read()

def write_file(file_path, content):
    with open(file_path, 'w') as file:
        file.write(content)

file_path = 'example.txt'
write_file(file_path, 'Hello, world!')
content = read_file(file_path)
print(content)

模块

在 Python 中,模块是组织代码的一种方式,它将相关的代码分组到一个文件中,从而提高代码的可重用性和可维护性。每个以 .py 结尾的文件都是一个模块。模块可以包含函数、类、变量以及可执行的代码。

导入模块

Python 提供了几种导入模块的方法:

  1. 使用 import 导入整个模块
import module_name

module_name.function_name()
module_name.variable_name

示例:

import math

print(math.sqrt(16))  # 输出: 4.0
  1. 使用 from ... import 导入模块的特定部分
from module_name import function_name, variable_name

function_name()
variable_name

示例:

from math import sqrt

print(sqrt(16))  # 输出: 4.0
  1. 使用 from ... import * 导入模块的所有内容
from module_name import *

function_name()
variable_name

示例:

from math import *

print(sqrt(16))  # 输出: 4.0

创建模块

创建模块非常简单,只需将代码保存到一个以 .py 结尾的文件中。例如,创建一个名为 mymodule.py 的模块:

# mymodule.py

def greet(name):
    print(f"Hello, {name}!")

pi = 3.14159

然后可以在另一个 Python 文件中导入并使用这个模块:

import mymodule

mymodule.greet("Alice")  # 输出: Hello, Alice!
print(mymodule.pi)       # 输出: 3.14159

模块的搜索路径

当导入模块时,Python 会按照 sys.path 中列出的目录顺序进行搜索。sys.path 是一个包含目录路径的列表。可以通过 sys 模块查看或修改搜索路径:

import sys

print(sys.path)  # 输出模块搜索路径列表

可以向 sys.path 添加新的目录,使 Python 能够找到自定义模块:

import sys

sys.path.append('/path/to/my/modules')

内置模块和第三方模块

Python 附带了许多内置模块,如 mathsysos 等。除了内置模块,还可以安装和使用第三方模块。使用 pip 工具可以方便地安装第三方模块:

pip install module_name

安装第三方模块后,可以像导入内置模块一样导入它们。

包是包含多个模块的目录。目录下必须有一个名为 __init__.py 的文件,表示该目录是一个包。可以在包中创建子包,形成层次结构。

包结构示例:

my_package/
    __init__.py
    module1.py
    module2.py

使用包时,可以按以下方式导入模块:

from my_package import module1

module1.some_function()

或者:

import my_package.module1

my_package.module1.some_function()

示例:创建和使用包

创建一个包 my_package,并包含两个模块 module1.pymodule2.py

my_package/
    __init__.py
    module1.py
    module2.py

module1.py 内容:

# module1.py

def func1():
    print("Function 1 from module 1")

module2.py 内容:

# module2.py

def func2():
    print("Function 2 from module 2")

在另一个文件中使用这些模块:

from my_package import module1, module2

module1.func1()  # 输出: Function 1 from module 1
module2.func2()  # 输出: Function 2 from module 2

通过这些示例和解释,可以更好地理解 Python 中的模块及其使用方法,帮助组织和重用代码。

sys 模块

Python 的 sys 模块提供了访问与 Python 解释器相关的变量和函数。它是 Python 标准库的一部分,用于与解释器进行交互和控制。

导入 sys 模块

import sys

常用功能和属性

1. sys.argv

sys.argv 是一个列表,包含命令行参数。第一个元素是脚本名,后续元素是传递给脚本的命令行参数。

import sys

print("Script name:", sys.argv[0])
for i, arg in enumerate(sys.argv[1:]):
    print(f"Argument {i + 1}: {arg}")

运行此脚本:

$ python script.py arg1 arg2
Script name: script.py
Argument 1: arg1
Argument 2: arg2
2. sys.exit()

sys.exit() 用于退出程序,可以传递一个退出状态码(0 表示成功,非 0 表示错误)。

import sys

if len(sys.argv) < 2:
    print("Usage: python script.py <name>")
    sys.exit(1)

name = sys.argv[1]
print(f"Hello, {name}!")
3. sys.path

sys.path 是一个列表,指定了模块的搜索路径。当您导入模块时,Python 会按照 sys.path 中的路径顺序进行搜索。

import sys

print("Module search paths:")
for path in sys.path:
    print(path)

您可以向 sys.path 添加新的路径:

sys.path.append('/path/to/my/modules')
4. sys.stdin, sys.stdout, sys.stderr

这些属性表示标准输入、标准输出和标准错误流,分别对应于程序的输入、输出和错误流。

sys.stdin 示例:
import sys

print("Please enter some input:")
input_text = sys.stdin.read()
print("You entered:", input_text)
sys.stdoutsys.stderr 示例:
import sys

sys.stdout.write("This is standard output.\n")
sys.stderr.write("This is standard error.\n")
5. sys.version

sys.version 返回 Python 解释器的版本信息。

import sys

print("Python version:", sys.version)
6. sys.platform

sys.platform 返回当前操作系统的平台标识。

import sys

print("Platform:", sys.platform)
7. sys.getsizeof()

sys.getsizeof() 返回对象的内存大小(以字节为单位)。

import sys

a = 123
print("Size of a:", sys.getsizeof(a))

b = [1, 2, 3, 4, 5]
print("Size of b:", sys.getsizeof(b))
8. sys.modules

sys.modules 是一个字典,包含了所有已经导入的模块。

import sys

print("Currently imported modules:")
for module in sys.modules:
    print(module)

示例总结

以下是一个综合示例,展示了 sys 模块的一些常见用法:

import sys

def main():
    print("Script name:", sys.argv[0])
    
    if len(sys.argv) < 2:
        print("Usage: python script.py <name>")
        sys.exit(1)
    
    name = sys.argv[1]
    print(f"Hello, {name}!")

    print("\nModule search paths:")
    for path in sys.path:
        print(path)

    print("\nPython version:", sys.version)
    print("Platform:", sys.platform)
    print("Size of sys.argv:", sys.getsizeof(sys.argv))
    print("Standard output example.")
    sys.stderr.write("Standard error example.\n")

if __name__ == "__main__":
    main()

运行此脚本:

$ python script.py Alice

输出:

Script name: script.py
Hello, Alice!

Module search paths:
/path/to/current/directory
/usr/lib/python3.8
...

Python version: 3.8.10 (default, Jun  4 2021, 15:09:15) 
[GCC 7.5.0]
Platform: linux
Size of sys.argv: 88
Standard output example.
Standard error example.

os 模块

Python 的 os 模块提供了一种使用操作系统功能的方式,使我们可以与操作系统进行交互。os 模块中的许多函数是跨平台的,允许我们编写适用于多个操作系统的代码。

导入 os 模块

import os

常用功能和属性

1. 获取和修改当前工作目录
  • 获取当前工作目录: os.getcwd()
  • 修改当前工作目录: os.chdir(path)
import os

# 获取当前工作目录
current_dir = os.getcwd()
print("Current Directory:", current_dir)

# 修改当前工作目录
os.chdir('/path/to/new/directory')
print("Changed Directory:", os.getcwd())
2. 文件和目录操作
  • 创建目录: os.mkdir(path)
  • 递归创建目录: os.makedirs(path)
  • 删除目录: os.rmdir(path)
  • 递归删除目录: os.removedirs(path)
  • 列出目录内容: os.listdir(path)
import os

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

# 创建多级目录
os.makedirs('new_directory/sub_directory')

# 列出目录内容
print("Directory Contents:", os.listdir('.'))

# 删除目录
os.rmdir('new_directory/sub_directory')

# 删除多级目录
os.removedirs('new_directory')
3. 文件操作
  • 删除文件: os.remove(path)
  • 重命名文件: os.rename(old, new)
import os

# 创建文件并写入内容
with open('example.txt', 'w') as file:
    file.write('Hello, world!')

# 重命名文件
os.rename('example.txt', 'renamed_example.txt')

# 删除文件
os.remove('renamed_example.txt')
4. 路径操作
  • 检查路径是否存在: os.path.exists(path)
  • 检查是否是文件: os.path.isfile(path)
  • 检查是否是目录: os.path.isdir(path)
  • 获取文件的绝对路径: os.path.abspath(path)
  • 分割路径: os.path.split(path)
  • 获取文件名: os.path.basename(path)
  • 获取目录名: os.path.dirname(path)
  • 连接路径: os.path.join(path, *paths)
import os

# 检查路径是否存在
print("Path exists:", os.path.exists('example.txt'))

# 获取文件的绝对路径
print("Absolute Path:", os.path.abspath('example.txt'))

# 分割路径
print("Split Path:", os.path.split('/path/to/example.txt'))

# 获取文件名和目录名
print("Base Name:", os.path.basename('/path/to/example.txt'))
print("Directory Name:", os.path.dirname('/path/to/example.txt'))

# 连接路径
print("Joined Path:", os.path.join('/path', 'to', 'example.txt'))
5. 环境变量
  • 获取环境变量: os.getenv(key, default=None)
  • 设置环境变量: os.putenv(key, value)
  • 获取所有环境变量: os.environ
import os

# 获取环境变量
path = os.getenv('PATH')
print("PATH:", path)

# 设置环境变量
os.putenv('MY_VAR', 'my_value')

# 获取所有环境变量
print("All Environment Variables:")
for key, value in os.environ.items():
    print(f"{key}: {value}")
6. 执行系统命令
  • 执行系统命令: os.system(command)
import os

# 执行系统命令
os.system('ls -l')

示例总结

以下是一个综合示例,展示了 os 模块的一些常见用法:

import os

# 获取当前工作目录
current_dir = os.getcwd()
print("Current Directory:", current_dir)

# 创建和删除目录
os.mkdir('test_dir')
os.makedirs('test_dir/sub_dir')
print("Directory Contents after creation:", os.listdir('test_dir'))
os.rmdir('test_dir/sub_dir')
os.removedirs('test_dir')

# 文件操作
with open('example.txt', 'w') as file:
    file.write('Hello, world!')

os.rename('example.txt', 'renamed_example.txt')
os.remove('renamed_example.txt')

# 路径操作
print("Path exists:", os.path.exists('example.txt'))
print("Absolute Path:", os.path.abspath('example.txt'))
print("Split Path:", os.path.split('/path/to/example.txt'))
print("Base Name:", os.path.basename('/path/to/example.txt'))
print("Directory Name:", os.path.dirname('/path/to/example.txt'))
print("Joined Path:", os.path.join('/path', 'to', 'example.txt'))

# 环境变量
print("PATH:", os.getenv('PATH'))
os.putenv('MY_VAR', 'my_value')
print("All Environment Variables:")
for key, value in os.environ.items():
    print(f"{key}: {value}")

# 执行系统命令
os.system('echo Hello, world!')

补充—— b 模式

在 Python 中,文件打开模式中的 'b' 表示二进制模式。与文本模式不同,二进制模式用于处理二进制数据,如图像、视频、音频文件等。使用二进制模式时,文件内容以字节(bytes)而非字符串的形式读取和写入。

二进制模式的使用

常见的二进制模式:

  • 'rb': 以二进制模式读取文件。
  • 'wb': 以二进制模式写入文件,如果文件不存在则创建,如果存在则清空。
  • 'ab': 以二进制模式追加文件内容。

示例代码

读取二进制文件

在二进制模式下读取文件时,文件内容以字节对象(bytes)的形式返回。

with open('example.bin', 'rb') as file:
    content = file.read()
    print(content)  # 输出文件的字节内容
写入二进制文件

在二进制模式下写入文件时,需要写入字节对象。

with open('example.bin', 'wb') as file:
    content = b'\x00\x01\x02\x03\x04'  # 字节对象
    file.write(content)
追加二进制文件内容

在二进制模式下追加内容时,新的内容将被附加到文件末尾。

with open('example.bin', 'ab') as file:
    additional_content = b'\x05\x06\x07'
    file.write(additional_content)

示例:复制二进制文件

以下示例展示了如何复制一个二进制文件:

source_path = 'source.bin'
destination_path = 'destination.bin'

# 读取源文件的二进制内容
with open(source_path, 'rb') as source_file:
    content = source_file.read()

# 将内容写入目标文件
with open(destination_path, 'wb') as destination_file:
    destination_file.write(content)

注意事项

  1. 区分文本模式和二进制模式:

    • 在文本模式下,读取和写入的是字符串,Python 会自动处理文件编码(默认编码通常是 UTF-8)。
    • 在二进制模式下,读取和写入的是字节,数据不经过任何编码转换。
  2. 处理图像、音频等文件:

    • 对于需要精确处理的二进制数据(如图像、音频文件),必须使用二进制模式。
  3. 跨平台差异:

    • 在 Windows 系统上,文本模式会将换行符 \n 转换为 \r\n,而在二进制模式下,不会进行这种转换。因此,对于处理精确数据的场景,建议使用二进制模式以确保数据的一致性。

示例:处理图像文件

以下示例展示了如何读取和写入图像文件:

# 读取图像文件
with open('example.jpg', 'rb') as image_file:
    image_data = image_file.read()

# 写入图像文件
with open('copy_example.jpg', 'wb') as copy_image_file:
    copy_image_file.write(image_data)

补充—— flush

在 Python 中,flush() 函数用于刷新文件的内部缓冲区,将缓冲区中的数据强制写入文件。这对于确保数据及时写入文件系统非常重要,尤其是在处理日志文件或其他需要及时更新的文件时。

基本用法

flush() 函数没有参数,它直接作用于文件对象:

file.flush()

使用场景

  • 日志记录:在记录日志时,频繁调用 flush() 可以确保日志信息及时写入磁盘,即使程序崩溃或意外终止,也不会丢失重要的日志信息。
  • 实时数据处理:在需要实时更新文件内容的场景下,如实时数据记录或输出。
  • 长时间运行的程序:在长时间运行的程序中,定期调用 flush() 可以减少数据丢失的风险。

示例代码

以下是一些使用 flush() 函数的示例:

写入文件并刷新缓冲区
with open('example.txt', 'w') as file:
    file.write('Hello, world!\n')
    file.flush()  # 将缓冲区中的数据强制写入文件
实时记录日志
import time

with open('log.txt', 'w') as log_file:
    for i in range(5):
        log_file.write(f'Log entry {i}\n')
        log_file.flush()  # 确保每条日志立即写入文件
        time.sleep(1)  # 模拟一些处理时间
配合 sys.stdout 使用

在命令行程序中,有时需要确保标准输出立即显示,而不是被缓冲。

import sys
import time

for i in range(5):
    print(f'Output {i}', end='\n', flush=True)
    time.sleep(1)  # 模拟一些处理时间

在这个例子中,flush=True 参数确保每次输出都会立即刷新到控制台,而不是等待缓冲区满了再输出。

注意事项

  1. 性能影响:频繁调用 flush() 可能会影响性能,因为每次调用都会进行磁盘写操作。应根据实际需求平衡及时写入和性能之间的关系。
  2. 自动刷新:使用 with 语句管理文件时,当 with 语句块结束时,文件会自动关闭并刷新缓冲区,因此在这种情况下通常不需要手动调用 flush()
  3. 缓冲模式:在打开文件时,可以通过 buffering 参数设置缓冲模式。默认缓冲模式通常已经足够,但在特殊情况下可以调整。例如,buffering=1 表示行缓冲,buffering=0 表示无缓冲。

示例:实时数据记录

import time

# 模拟实时数据记录
with open('data.txt', 'w') as data_file:
    for i in range(10):
        data_file.write(f'Data point {i}\n')
        data_file.flush()  # 确保每个数据点立即写入文件
        time.sleep(0.5)  # 模拟数据生成时间间隔
  • 29
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值