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

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()

也可以获取父类的属性。

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)  # 子类的属性

遵循方法解析顺序,菱形的多继承中如果没使用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__

使用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__

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;
}

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(); // 多态调用
        }
    }
}

但是在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!"

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} 会把


2、受保护的属性

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

class Animal(object):
    def __init__(self):
        self._name = 'Dog' # 受保护的属性,但不建议在外部直接访问


a = Animal()
print(a._name) # 可以直接访问

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)

另一种写法

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)

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:[email protected]: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()

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)

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

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

class absint(int):
    # 取整数绝对值
    def __new__(cls, value):
        return super().__new__(cls, abs(value))


a = absint(-1)
print(a)

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

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

你可能感兴趣的:(Python,python,开发语言)