• python面向对象编程:类和对象


    1、封装

    封装是一种将属性和方法组合在一个单元中,从而隐藏对象的内部状态并保护其数据。

    2、继承和多继承

    继承是指一个类可以继承另一个类的属性和方法,从而减少代码的重复性。

    1、多重继承和方法解析顺序(MRO)

    mro采用DFS时菱形继承就会有问题,采用BFS时正常继承就会有问题,所以采用C3算法同时满足正常继承和菱形继承。

    class A:
        def show(self):
            print("A")
    
    
    class B(A):
        def show(self):
            print("B")
    
    
    class C(A):
        def show(self):
            print("C")
    
    # 会优先查找B中是否有show方法
    class D(B, C):
        pass
    
    
    d = D()
    d.show()  # 输出 "B"
    print(D.__mro__) # 查看MRO
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2、super

    super是一个内置函数,用于在子类中调用父类的方法。

    
    class Animal:
        def method(self):
            print("Animal's method")
    
    
    class Dog(Animal):
        def method(self):
            super().method()  # 重写后,先调用父类的方法
            print("Dog's method")
    
    
    d = Dog()
    d.method()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    也可以获取父类的属性。

    class Parent:
        def __init__(self, name, score):
            self.name = name
            self.score = score
    
    
    class Child(Parent):
        def __init__(self, name, age, score):
            super().__init__(name, score)  # 调用父类的构造函数
            self.age = age
    
    
    child = Child("Alice", 30, 100)
    print(child.name)  # 获取父类的属性
    print(child.score)  # 获取父类的属性
    print(child.age)  # 子类的属性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    遵循方法解析顺序,菱形的多继承中如果没使用super函数

    class Base:
        def __init__(self):
            print('Base.__init__')
    
    
    class A(Base):
        def __init__(self):
            Base.__init__(self)
            print('A.__init__')
    
    
    class B(Base):
        def __init__(self):
            Base.__init__(self)
            print('B.__init__')
    
    
    class C(A, B):
        def __init__(self):
            A.__init__(self)
            B.__init__(self)
            print('C.__init__')
    
    
    c = C()
    # 结果 会发现Base.__init__被调用两次
    Base.__init__
    A.__init__
    Base.__init__
    B.__init__
    C.__init__
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    使用super函数进行多继承,遵循MRO

    class Base:
        def __init__(self):
            print('Base.__init__')
    
    
    class A(Base):
        def __init__(self):
            super().__init__()
            print('A.__init__')
    
    
    class B(Base):
        def __init__(self):
            super().__init__()
            print('B.__init__')
    
    
    class C(A, B):
        def __init__(self):
            super().__init__()  # Only one call to super() here
            print('C.__init__')
    c = C()
    Base.__init__
    B.__init__
    A.__init__
    C.__init__
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3、多态和鸭子类型

    https://docs.python.org/zh-cn/3.7/glossary.html#term-duck-typing
    多态是指在不同的情况下,同一个方法可以具有不同的表现形式。在C++和Java中多态是和继承结合在一起的,所以需要三个条件:有继承关系、子类重写父类方法、父类引用指向子类对象。

    #include 
    
    class Animal {
    public:
        virtual void speak() {
            std::cout << "Animal speaks" << std::endl;
        }
    };
    
    class Dog : public Animal {
    public:
        void speak() override {
            std::cout << "Dog barks" << std::endl;
        }
    };
    
    class Cat : public Animal {
    public:
        void speak() override {
            std::cout << "Cat meows" << std::endl;
        }
    };
    
    int main() {
        Animal* animals[2]; # 必须是子类
        animals[0] = new Dog();
        animals[1] = new Cat();
    
        for (int i = 0; i < 2; i++) {
            animals[i]->speak(); // 多态调用
        }
    
        delete animals[0];
        delete animals[1];
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    Java的多态

    class Animal {
        public void speak() {
            System.out.println("Animal speaks");
        }
    }
    
    class Dog extends Animal {
        public void speak() {
            System.out.println("Dog barks");
        }
    }
    
    class Cat extends Animal {
        public void speak() {
            System.out.println("Cat meows");
        }
    }
    
    public class PolymorphismExample {
        public static void main(String[] args) {
            Animal[] animals = new Animal[2];
            animals[0] = new Dog();
            animals[1] = new Cat();
    
            for (int i = 0; i < 2; i++) {
                animals[i].speak(); // 多态调用
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    但是在Python,关注的不是传入对象是否是某个类的子类(不需要有继承关系),而是传入的对象是否有这个要执行的方法,如果有就执行,这就是鸭子类型。
    鸭子类型:当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
    鸭子类型是一种动态类型检查的概念,它关注对象的行为而不是类型,如果传入的对象的有你要执行的方法,那么就认为这个传入的对象是函数参数的合格“子类”或者说“同类”。比如file,StringIO,socket对象都支持read/write方法,再比如定义了__iter__魔术方法的对象可以用for迭代。

    class Animal:
        def __init__(self, name):
            self.name = name
        
        def speak(self):
            pass
    
    class Dog(Animal):
        def speak(self):
            return f"{self.name} says Woof!"
    
    class Cat(Animal):
        def speak(self):
            return f"{self.name} says Meow!"
    
    def animal_sound(animal):
        return animal.speak() # 只需animal对象有speak方法即可
    
    dog = Dog("Buddy")
    cat = Cat("Whiskers")
    
    print(animal_sound(dog))  # 输出 "Buddy says Woof!"
    print(animal_sound(cat))  # 输出 "Whiskers says Meow!"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    4、私有属性

    对象的属性是对象所包含的数据或状态信息。对象属性可以通过点号.来访问。Python中有两种主要类型的属性:实例属性和类属性,属性可以是只读的、可写的或可计算的,这取决于它们的实现方式和用途。

    1、私有属性

    在Python中,私有属性是指以两个下划线 __ 开头的属性,例如 __name。私有属性的命名约定使其在类的外部不容易直接访问,但实际上仍然可以访问。Python 使用一种称为“名称修饰”(Name Mangling)的技术来改变私有属性的名称,以增加访问的难度,但仍然不是完全私有的。

    class Animal(object):
        def __init__(self):
            self.__age = 3	# 私有属性,会把属性__age变成_Animal__age并且放到__dict__中
        
        def age(self):
            return self.__age
    
    
    a = Animal()
    # a.__age 直接调用会报错
    
    # 使用名称修饰来访问私有属性
    print(a._Animal__age)
    
    # 推荐的方式是使用公共方法来访问私有属性
    print(a.age())
    
    print(a.__dict__) # {'_Animal__age': 3} 会把
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2、受保护的属性

    单个下划线前缀的属性被视为“受保护”的属性。这并不是强制性的,只是一种命名约定,用于指示属性不应该在类的外部直接访问。这种约定告诉其他开发人员,属性虽然不是私有的,但应该被视为内部实现的一部分,而不是公共接口的一部分。但是Python解释器不会对使用单下划线的属性做特殊处理。

    class Animal(object):
        def __init__(self):
            self._name = 'Dog' # 受保护的属性,但不建议在外部直接访问
    
    
    a = Animal()
    print(a._name) # 可以直接访问
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、property装饰器

    Python中用于创建属性的一种特殊方法,它允许您在访问属性时执行自定义的逻辑,而不仅仅是简单地获取或设置属性的值。property 主要用于实现属性的封装和访问控制,以确保属性的安全性和一致性。

    class Animal(object):
        def __init__(self, eat):
            self.__eat = eat
    
        # 只有@property时属性不能赋值操作
        @property
        def eat(self):
            return self.__eat
    
        @eat.setter
        def eat(self, value):
            # 设置属性值,同时可以做校验、计算等
            if not isinstance(value, str):
                raise TypeError('Expected a string')
            self.__eat = value
    
        @eat.deleter
        def eat(self):
            del self.__eat
    
    
    a = Animal('rot')
    print(a.eat)
    a.eat = 'cao'
    print(a.eat)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    另一种写法

    class Animal(object):
        def __init__(self, eat):
            self.__eat = eat
    
        def get_eat(self):
            return self.__eat
    
        def set_eat(self, value):
            # 设置属性值,同时可以做校验、计算等
    
            if not isinstance(value, str):
                raise TypeError('Expected a string')
            self.__eat = value
    
        def del_eat(self):
            del self.__eat
    
        eat = property(get_eat, set_eat, del_eat)
    
    
    a = Animal('rot')
    print(a.eat)
    a.eat = 'cao'
    print(a.eat)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    5、私有方法

    没有真正的私有方法(Private Methods),但有一种命名约定来表示方法应该被视为私有的,即方法名称以一个或多个下划线 _ 开头。

    6、类方法

    https://docs.python.org/zh-cn/3.7/library/functions.html#classmethod
    1、使用@classmethod装饰的方法,第一个参数表示类对象,通常命名为cls表示当前类。
    2、类方法中只能调用类变量,不能调用实例变量,因为没有self参数。
    3、既可以通过对象名.类方法名来访问,也可以通过类名.类方法名来访问,建议使用类名.类方法名。
    4、类方法通常用于定义与该类相关而与具体对象无关的操作。
    5、通过类方法可以实现多构造器的场景。

    # python的redis第三方库中,使用url连接redis时定义的from_url是一个类方法。
    class Redis(object):
    	""""""
        @classmethod
        def from_url(cls, url, db=None, **kwargs):
            """"""
            connection_pool = ConnectionPool.from_url(url, db=db, **kwargs)
            return cls(connection_pool=connection_pool)
    
    # 调用类方法
    redis_ints = Redis.from_url('redis://user:password@127.0.0.1:6379/0')
    
    # python的datetime模块中
    class date:
        @classmethod
        def fromtimestamp(cls, t):
            "Construct a date from a POSIX timestamp (like time.time())."
            y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
            return cls(y, m, d)
    
        @classmethod
        def today(cls):
            "Construct a date from time.time()."
            t = _time.time()
            return cls.fromtimestamp(t)
    
    # 调用类方法
    date.today()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    7、静态方法

    https://docs.python.org/zh-cn/3.7/library/functions.html#staticmethod
    1、使用@staticmethod装饰的方法,无self参数,与普通的函数类似。
    2、静态方法中只能调用类成员,不能调用实例成员。
    3、既可以通过对象名.静态法名来访问,也可以通过类名.静态法名来访问。
    4、静态方法主要作为一些工具方法,通常与类和对象无关,有一些跟类有关系的功能,但在运行时又不需要实例和类参与的情况。

    # 定义类
    class date:
        def __new__(cls, year, month, day, *args, **kwargs):
            assert isinstance(year, int)
            assert isinstance(month, int)
            assert isinstance(day, int)
            self = object.__new__(cls)
            self._day = day
            self._month = month
            self._year = year
            return self
    
    d = date(1992, 12, 1)
    print(d._day)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    8、init__和__new

    init()是初始化方法,初始化实例的时候自动调用,方法有一个参数 self,代表当前创建的实例对象。__init__方法在 __new__方法的基础上完成一些初始化工作,不需要返回值。
    new()是一个静态方法,当实例化一个类对象时,最先被调用的是 new 方法。该方法第一个参数 cls 表示当前要实例化的类。__new__方法必须要有返回值,是实例化对象(即self传给__init__方法)。
    new()使用场景:
    1、单例模式

    class Singleton(object):
        def __new__(cls, *args, **kwargs):
            # 实例化类的时候,可以用于确保一个类只有一个实例存在
            if not hasattr(cls, '_instance'):
                cls._instance = super().__new__(cls, *args, **kwargs)
            return cls._instance
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、继承一些不可变的类时

    class absint(int):
        # 取整数绝对值
        def __new__(cls, value):
            return super().__new__(cls, abs(value))
    
    
    a = absint(-1)
    print(a)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    9、接口

    Python中没有接口,需要借助第三方模块,接口不能实例化,只能被继承

    from abc import ABC, ABCMeta, abstractmethod
    
    class Shape(ABC):
        @abstractmethod
        def area(self):
            pass
    
    class Circle(Shape):
        def __init__(self, radius):
            self.radius = radius
    
        def area(self):
            return 3.14 * self.radius ** 2
    
    class Rectangle(Shape):
        def __init__(self, width, height):
            self.width = width
            self.height = height
    
        def area(self):
            return self.width * self.height
    
    circle = Circle(5)
    rectangle = Rectangle(4, 6)
    
    print(circle.area())      # 输出: 78.5
    print(rectangle.area())   # 输出: 24
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    10、Mixin类

    用于通过多继承的方式在类之间共享方法和属性,是用来扩展其他类的功能。Mixin 类通常包含一组方法或属性,这些方法和属性可以被其他类继承并复用,从而实现代码重用和组合的目的。

    class CreateMixin:
        def put(self):
            pass
    
    
    class UpdateMixin:
        def post(self):
            pass
    
    class DeleteMixin:
        def delete(self):
            pass
    
    class UserInfoApi(CreateMixin, UpdateMixin, DeleteMixin):
        # 可以共享 Mixin功能
        def get(self):
            pass
    
    class CompanyApi(CreateMixin, UpdateMixin, DeleteMixin):
        def get(self):
            pass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    python复习(2022年8月)
    SpringBoot未授权访问漏洞利用方法
    Mysql数据库面经
    ElasticSearch从入门到精通:常用操作
    VUE3子表格嵌套分页查询互相干扰的问题解决
    北大肖臻老师《区块链技术与应用》系列课程学习笔记[2]比特币的共识协议
    msigdbr hallmarks gsea broad研究所
    跨环境同步数据的操作方式:使用mysqldump 远程执行备份与还原
    springboot毕设项目城市地铁线路与站点查询系统528h5(java+VUE+Mybatis+Maven+Mysql)
    HarmonyOS DevEso环境搭建
  • 原文地址:https://blog.csdn.net/u010442378/article/details/133977269