学习日志08 python

嘎嘎嘎嘎嘎今天知道了黑客松,实在是太燃了,这比挑战杯什么老牌的比赛更重视技术,非常燃啊!!!!

好了,白天不知道在摸什么鱼,冲啊,好想看科技会展啊,感觉是很新的科技展会,太有活力了!(7.7写,果然是还没“上班”的活力劲头)

干了两天活,做志愿者“上班”做老师,纯纯牛马,这让我无比珍惜现在的大学生身份。发现自然流出扑克脸一样班味表情,丧失和人沟通分享欲望变成了很寻常的事情,一种模拟体验社会毒打的暑假实践......

1 Python 面向对象编程知识笔记

一、类与对象基础

  1. 类的定义

    • 语法:class ClassName:
    • 类是对象的模板,包含属性(变量)和方法(函数)。
    • 示例:

      python

      运行

      class Car:
          wheels = 4  # 类属性(所有实例共享)
      
  2. 对象实例化

    • 通过调用类创建对象:obj = ClassName()
    • 每个对象独立拥有实例属性。
    • 示例:

      python

      运行

      my_car = Car()
      print(my_car.wheels)  # 输出: 4
      
  3. __init__ 构造方法

    • 初始化对象属性,创建对象时自动调用。
    • 第一个参数必须是 self,指向实例本身。
    • 示例:

      python

      运行

      class Car:
          def __init__(self, color, brand):
              self.color = color  # 实例属性
              self.brand = brand
      

二、属性与方法类型

  1. 实例属性与方法

    • 实例属性:每个对象独有的数据(通过 self 访问)。
    • 实例方法:操作实例数据的函数,第一个参数为 self
    • 示例:

      python

      运行

      class Car:
          def __init__(self, speed):
              self.speed = speed  # 实例属性
          
          def accelerate(self, increment):
              self.speed += increment  # 实例方法
      
  2. 类属性与类方法

    • 类属性:所有对象共享的属性(定义在类内部,方法外部)。
    • 类方法:操作类属性的函数,使用 @classmethod 装饰器,第一个参数为 cls
    • 示例:

      python

      运行

      class Car:
          total_cars = 0  # 类属性
          
          @classmethod
          def add_car(cls):
              cls.total_cars += 1  # 类方法
      
  3. 静态方法

    • 不依赖类或实例的工具方法,使用 @staticmethod 装饰器。
    • 示例:

      python

      运行

      class Car:
          @staticmethod
          def convert_mph_to_kph(mph):
              return mph * 1.609  # 静态方法
      

三、继承与多态

  1. 单继承

    • 子类继承父类的属性和方法。
    • 语法:class Child(Parent):
    • 示例:

      python

      运行

      class Vehicle:
          def move(self):
              print("Moving...")
      
      class Car(Vehicle):  # 继承 Vehicle
          pass
      
  2. 方法重写

    • 子类覆盖父类的方法。
    • 示例:

      python

      运行

      class Car(Vehicle):
          def move(self):
              print("Driving...")  # 重写父类方法
      
  3. 多继承

    • 子类继承多个父类的属性和方法。
    • 语法:class Child(Parent1, Parent2):
    • 注意:可能引发菱形继承问题(通过 MRO 解决)。
    • 示例:

      python

      运行

      class Flyable:
          def fly(self):
              print("Flying...")
      
      class FlyingCar(Vehicle, Flyable):  # 多继承
          pass
      
  4. 多态

    • 不同子类对同一方法的不同实现。
    • 示例:

      python

      运行

      for vehicle in [Vehicle(), Car()]:
          vehicle.move()  # 输出不同结果
      

四、封装与访问控制

  1. 私有属性与方法

    • 使用双下划线 __ 前缀定义私有成员(外部无法直接访问)。
    • 示例:

      python

      运行

      class BankAccount:
          def __init__(self, balance):
              self.__balance = balance  # 私有属性
          
          def __withdraw(self, amount):  # 私有方法
              self.__balance -= amount
      
  2. 属性访问控制

    • 使用 @property 装饰器创建只读属性。
    • 使用 @attr.setter 装饰器创建可写属性。
    • 示例:

      python

      运行

      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:
                  self.__radius = value
      

五、特殊方法(魔术方法)

  1. 对象表示

    • __str__():返回对象的用户友好字符串表示(str() 调用)。
    • __repr__():返回对象的开发者友好字符串表示(调试时使用)。
    • 示例:

      python

      运行

      class Point:
          def __init__(self, x, y):
              self.x = x
              self.y = y
          
          def __str__(self):
              return f"({self.x}, {self.y})"
      
  2. 运算符重载

    • __add__():定义 + 运算符行为。
    • __len__():定义 len() 函数行为。
    • 示例:

      python

      运行

      class Vector:
          def __init__(self, x, y):
              self.x = x
              self.y = y
          
          def __add__(self, other):
              return Vector(self.x + other.x, self.y + other.y)
      
  3. 对象比较

    • __eq__():定义 == 运算符行为。
    • __lt__():定义 < 运算符行为。
    • 示例:

      python

      运行

      class Person:
          def __init__(self, age):
              self.age = age
          
          def __eq__(self, other):
              return self.age == other.age
      

六、高级特性

  1. 抽象基类(ABC)

    • 定义接口,强制子类实现特定方法。
    • 使用 abc 模块的 @abstractmethod 装饰器。
    • 示例:

      python

      运行

      from abc import ABC, abstractmethod
      
      class Shape(ABC):
          @abstractmethod
          def area(self):
              pass
      
  2. Mixin 类

    • 提供特定功能的小型类,用于多重继承。
    • 示例:

      python

      运行

      class LoggableMixin:
          def log(self):
              print(f"Logging: {self.__class__.__name__}")
      
  3. 类的动态特性

    • 动态添加属性和方法:

      python

      运行

      class MyClass:
          pass
      
      obj = MyClass()
      obj.new_attr = 42  # 动态添加属性
      

七、实例与类的区别

特性实例
数据存储存储对象特定的数据存储所有实例共享的数据
访问方式通过对象名访问通过类名访问
修改影响仅影响当前对象影响所有实例
典型用途存储个体差异的属性存储类级别的常量或计数器

八、设计模式示例

  1. 单例模式

    • 确保类只有一个实例,并提供全局访问点。
    • 实现方式:

      python

      运行

      class Singleton:
          _instance = None
          
          def __new__(cls):
              if not cls._instance:
                  cls._instance = super().__new__(cls)
              return cls._instance
      
  2. 工厂模式

    • 通过工厂方法创建对象,解耦对象的创建和使用。
    • 示例:

      python

      运行

      class AnimalFactory:
          @staticmethod
          def create_animal(type):
              if type == "dog":
                  return Dog()
              elif type == "cat":
                  return Cat()
      

九、常见错误与注意事项

  1. 混淆类属性和实例属性

    • 类属性由所有实例共享,修改会影响所有对象。
    • 示例:

      python

      运行

      class MyClass:
          x = []  # 类属性(共享列表)
      
      a = MyClass()
      b = MyClass()
      a.x.append(1)
      print(b.x)  # 输出: [1](意外共享)
      
  2. 多重继承的复杂性

    • 遵循 MRO(方法解析顺序)规则,使用 ClassName.mro() 查看顺序。
  3. 过度使用面向对象

    • 简单问题优先使用函数式编程,避免过度设计。

2 为什么要写运算符重载?

在 Python 里,运算符重载能够让你自定义类的对象通过运算符开展运算。

以 __add__() 方法为例,它的作用是重新定义 + 运算符的行为。

虽然 + 运算符原本就具备基础运算行为,不过在处理自定义类的对象时,其默认行为往往无法满足需求。

下面通过几个示例来详细说明这样做的原因:

1. 实现自定义加法逻辑

对于自定义类而言,直接使用 + 运算符会引发错误。这时就需要借助 __add__() 方法来定义特定的加法逻辑。

python

运行

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        # 定义向量加法:对应分量相加
        return Vector(self.x + other.x, self.y + other.y)

v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2  # 等价于 v1.__add__(v2)
print(v3.x, v3.y)  # 输出: 4 6

2. 支持混合类型运算

通过运算符重载,自定义类的对象能够和其他类型的对象进行运算。

python

运行

class Money:
    def __init__(self, amount, currency):
        self.amount = amount
        self.currency = currency

    def __add__(self, other):
        # 支持不同货币相加(简化示例)
        if isinstance(other, Money):
            if self.currency == other.currency:
                return Money(self.amount + other.amount, self.currency)
            else:
                # 实际应用中可能需要汇率转换
                raise ValueError("货币类型不匹配")
        elif isinstance(other, int) or isinstance(other, float):
            return Money(self.amount + other, self.currency)
        else:
            return NotImplemented

rmb1 = Money(100, "CNY")
rmb2 = Money(200, "CNY")
total = rmb1 + rmb2  # 同币种相加
print(total.amount)  # 输出: 300

3. 构建领域特定语言(DSL)

运算符重载可以让代码的表达更加自然,尤其在数学库的应用中表现突出。

python

运行

import numpy as np

class Matrix:
    def __init__(self, data):
        self.data = np.array(data)

    def __add__(self, other):
        # 矩阵加法
        return Matrix(self.data + other.data)

    def __mul__(self, other):
        # 矩阵乘法
        return Matrix(self.data @ other.data)

A = Matrix([[1, 2], [3, 4]])
B = Matrix([[5, 6], [7, 8]])
C = A + B  # 矩阵加法
D = A * B  # 矩阵乘法

总结

  • 默认行为局限Python 原生的 + 运算符只能处理内置类型,像 intstr 等,对于自定义类的对象并不适用。
  • 自定义运算规则:借助 __add__() 方法,我们可以让对象支持类似数学的自然运算。
  • 提升代码可读性:使用重载后的运算符,能让代码更加简洁明了,例如 a + b 比 a.add(b) 更易于理解。

在实际编程中,合理运用运算符重载可以使代码的接口更加直观,就如同内置类型的操作一样自然。

Q:什么叫做直接使用 + 运算符会引发错误?

A:

在 Python 中,如果你直接对自定义类的对象使用 + 运算符而没有定义 __add__() 方法,会引发 TypeError

这是因为 Python 不知道如何处理你自定义对象之间的加法运算。

以下是具体的解释和示例:

示例 1:未定义 __add__() 的类

假设你创建了一个简单的 Point 类表示二维平面上的点:

python

运行

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(1, 2)
p2 = Point(3, 4)
result = p1 + p2  # ❌ 直接使用 + 会报错

错误信息

plaintext

TypeError: unsupported operand type(s) for +: 'Point' and 'Point'

原因:Python 不知道如何将两个 Point 对象相加,因为你没有定义 __add__() 方法来告诉它该怎么做。

示例 2:定义 __add__() 后的类

如果你为 Point 类添加 __add__() 方法,就能定义加法行为:

python

运行

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        # 定义点的加法:坐标分别相加
        return Point(self.x + other.x, self.y + other.y)

p1 = Point(1, 2)
p2 = Point(3, 4)
result = p1 + p2  # ✅ 现在可以正常使用 + 运算符
print(result.x, result.y)  # 输出: 4 6

为什么会报错?

Python 的运算符本质上是方法调用的语法糖:

  • a + b 等价于 a.__add__(b)
  • a - b 等价于 a.__sub__(b)
  • 依此类推...

对于内置类型(如 intstrlist),Python 已经内置了这些方法的实现。

但对于自定义类,你需要自己实现这些方法,否则 Python 无法处理对应的运算。

总结

  • 直接使用 + 报错的原因:自定义类没有实现 __add__() 方法,Python 不知道如何处理该类对象的加法。
  • 解决方法:在类中定义 __add__() 方法,明确指定加法的行为。
  • 其他运算符:类似地,-*/ 等运算符也需要通过 __sub__()__mul__()__truediv__() 等方法来重载。

3 Python 的抽象基类(Abstract Base Class, ABC)和 Java 的抽象类(Abstract Class)

Python 的抽象基类(Abstract Base Class, ABC)和 Java 的抽象类(Abstract Class)在概念上有相似之处,但实现方式和使用场景存在差异。以下是详细对比:

核心概念对比

Java 抽象类
  • 定义:使用 abstract 关键字声明的类,不能被实例化,只能被继承。
  • 抽象方法:用 abstract 声明的方法,只有方法签名,没有实现,子类必须重写。
  • 特点
    • 可以包含抽象方法和具体方法。
    • 子类必须实现所有抽象方法,否则子类也必须声明为抽象类。

示例

java

abstract class Shape {
    // 抽象方法(没有方法体)
    public abstract double area();
    
    // 具体方法
    public void printType() {
        System.out.println("This is a shape.");
    }
}

class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    // 必须实现父类的抽象方法
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}
Python 抽象基类
  • 定义:通过 abc 模块创建,使用 @abstractmethod 装饰器声明抽象方法。
  • 抽象方法:必须在子类中实现的方法,否则子类无法实例化。
  • 特点
    • 可以包含抽象方法和具体方法。
    • 使用 register() 或子类化来注册实现类。
    • 支持鸭子类型(通过 @abstractmethod 强制实现,而非继承)。

示例

python

运行

from abc import ABC, abstractmethod

class Shape(ABC):
    # 抽象方法
    @abstractmethod
    def area(self):
        pass
    
    # 具体方法
    def print_type(self):
        print("This is a shape.")

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    # 必须实现基类的抽象方法
    def area(self):
        return 3.14 * self.radius ** 2

# 无法实例化抽象类
# s = Shape()  # TypeError: Can't instantiate abstract class Shape with abstract method area

主要区别

特性Java 抽象类Python 抽象基类
声明方式使用 abstract 关键字通过继承 ABC 类并使用 @abstractmethod
实例化限制不能直接实例化不能直接实例化(含未实现的抽象方法时)
抽象方法强制子类必须实现所有抽象方法子类必须实现所有抽象方法
多重继承不支持多重继承(只能继承一个抽象类)支持多重继承(可继承多个 ABC)
注册机制必须通过继承实现可通过 register() 动态注册非子类
静态类型检查编译时检查运行时检查
具体方法可以包含可以包含

Python 抽象基类的额外特性

1. 动态注册实现类

不通过继承,而是通过 register() 方法将类注册为抽象基类的 “虚拟子类”:

python

运行

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Square:
    def __init__(self, side):
        self.side = side
    
    def area(self):  # 实现了抽象方法,但未显式继承
        return self.side ** 2

# 动态注册
Shape.register(Square)

print(isinstance(Square(5), Shape))  # True
2. 抽象属性

使用 @property 和 @abstractmethod 组合声明抽象属性:

python

运行

from abc import ABC, abstractmethod

class Shape(ABC):
    @property
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    @property
    def area(self):  # 实现抽象属性
        return 3.14 * self.radius ** 2

总结

  • 相似点

    • 都不能直接实例化,需通过子类实现抽象方法。
    • 都可以包含抽象方法和具体方法。
  • 不同点

    • Python 的 ABC 更灵活,支持动态注册和鸭子类型,而 Java 的抽象类是静态类型系统的一部分。
    • Python 通过装饰器声明抽象方法,Java 使用 abstract 关键字。
    • Python 的检查发生在运行时,Java 在编译时检查。

两者的核心目的都是定义接口规范,但 Python 的实现更符合其动态语言的特性。

4 if __name__ == "__main__":   这种是什么意思?

if __name__ == "__main__": 是 Python 中一个非常重要的语法结构,用于区分代码是作为独立程序运行还是被其他模块导入。我来详细解释一下:

1. __name__ 变量的作用

每个 Python 文件(模块)都有一个内置的特殊变量 __name__,它的值取决于代码的运行方式:

  • 当文件作为独立程序直接运行时__name__ 的值会被自动设置为 "__main__"
  • 当文件被其他模块导入时__name__ 的值会被设置为该模块的名称(通常是文件名,不含 .py)。

2. if __name__ == "__main__": 的作用

这个条件语句用于判断当前模块是否是程序的入口点。

如果是,则执行缩进块中的代码;如果不是(即被导入),则跳过。

常见用途

  • 测试代码:在开发模块时,编写一些测试代码,但不希望这些代码在被导入时执行。
  • 独立运行:让模块既可以作为库被导入,也可以作为独立程序直接运行。

3. 示例说明

假设你有一个文件 math_utils.py

python

运行

# math_utils.py
def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

# 测试函数
def test():
    print(f"1 + 2 = {add(1, 2)}")
    print(f"5 - 3 = {subtract(5, 3)}")

# 判断是否作为独立程序运行
if __name__ == "__main__":
    test()

场景分析

  1. 直接运行 math_utils.py

    bash

    python math_utils.py
    
     

    输出:

    plaintext

    1 + 2 = 3
    5 - 3 = 2
    
     

    此时 __name__ == "__main__" 为 True,测试代码被执行。

  2. 在其他文件中导入 math_utils

    python

    运行

    # main.py
    import math_utils
    
    result = math_utils.add(10, 20)
    print(f"10 + 20 = {result}")
    
     

    输出:

    plaintext

    10 + 20 = 30
    
     

    此时 math_utils.py 中的测试代码不会执行,因为 __name__ 的值是 "math_utils",而不是 "__main__"

4. 为什么需要这种结构?

  • 避免副作用:模块中可能包含一些初始化代码或测试代码,不应该在被导入时执行。
  • 提高代码复用性:让模块既可以独立运行,也可以被其他程序引用,实现 “一次编写,多种用途”。

5. 在你的员工管理系统中

在之前的代码中,如果添加 if __name__ == "__main__":,可以将测试代码封装起来:

python

运行

# ... 前面的类定义保持不变 ...

# 测试代码
if __name__ == "__main__":
    manager = Manager("张三", "M001", 10000, 0.2)
    developer = Developer("李四", "D001", 8000, 3)

    print("经理信息:")
    manager.display_info()

    print("\n开发人员信息:")
    developer.display_info()

这样,当这个文件被其他模块导入时,测试代码不会执行,只有作为独立程序运行时才会执行。

总结

if __name__ == "__main__": 是 Python 中一个重要的最佳实践,用于控制代码的执行范围,让模块更加灵活和可复用。建议在编写可导入的模块时始终使用这种结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值