封装是一种将属性和方法组合在一个单元中,从而隐藏对象的内部状态并保护其数据。
继承是指一个类可以继承另一个类的属性和方法,从而减少代码的重复性。
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
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__
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!"
对象的属性是对象所包含的数据或状态信息。对象属性可以通过点号.来访问。Python中有两种主要类型的属性:实例属性和类属性,属性可以是只读的、可写的或可计算的,这取决于它们的实现方式和用途。
在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} 会把
单个下划线前缀的属性被视为“受保护”的属性。这并不是强制性的,只是一种命名约定,用于指示属性不应该在类的外部直接访问。这种约定告诉其他开发人员,属性虽然不是私有的,但应该被视为内部实现的一部分,而不是公共接口的一部分。但是Python解释器不会对使用单下划线的属性做特殊处理。
class Animal(object):
def __init__(self):
self._name = 'Dog' # 受保护的属性,但不建议在外部直接访问
a = Animal()
print(a._name) # 可以直接访问
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)
没有真正的私有方法(Private Methods),但有一种命名约定来表示方法应该被视为私有的,即方法名称以一个或多个下划线 _ 开头。
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()
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)
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)
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
用于通过多继承的方式在类之间共享方法和属性,是用来扩展其他类的功能。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