类
类可以看做是一个模版,或者图纸,系统根据类的定义来造出对象。我们要造一个汽车,怎么样造?类就是这个图纸,规定了汽车的详细信息,然后根据图纸将汽车造出来。python中类叫class,对象叫object,或者实例instance
将一个事物分解成状态和行为两部分,比如学生类,状态就是他的身份信息(姓名,学习,年龄...),行为(学习,写作业,玩...),将状态和行为组合到一起,这就是类。 在类中每个对象都会共享这个类的行为,但是状态是不共享的,比如每个学生都能学习,写作业,玩;但是每个学生的身份信息是不一致。
定义:
class Student:
# 构成方法:用于初始化对象的,self表示是哪一个对象,类似于指针,Java中的this
def __init__(self, name, score):
self.name = name
self.score = score
def say_score(self):
print(f"{self.name}的分数是:{self.score}")
s1 = Student("hhc", 149)
s1.say_score()
__ init __ 与 __ new __
init:是用于初始化对象的,一般在通过类创建对象时,都会传递参数进去,初始化当前对象的相关属性。第一个参数必须是self,指向创建的对象。如果我们不定义 init 方法,系统会提供一个默认的 init 方法。如果我们定义了带参的 init 方法,系统不创建默认的 init 方法
def __init__(self, name, score):
self.name = name # 实例属性
self.score = score
new:用于创建对象的,创建对象后self就指向的这个对象,如何再调用init方法。
实例属性
实例属性是从属于实例对象的属性,也称为“实例变量”。实例属性有三种定义方式
- init函数中(常见)
- 其他实例函数中 (实例属性存在则修改值,不存在则添加)
- 对象.实例属性 = 值 (实例属性存在则修改值,不存在则添加)
class Student:
def __init__(self, name, score):
# 方式一
self.name = name
self.score = score
def say_score(self):
# 方式二
self.age = 21
print(f"{self.name}的分数是:{self.score}")
s1 = Student("hhc", 149)
s1.say_score()
print(s1.age)
# 方式三
s1.salary = 3000
实例方法
方法调用:对象.方法名([ 实参列表 ])
要点:定义实例方法时,第一个参数必须为 self 。和前面一样, self 指当前的实例对象。调用实例方法时,不需要也不能给 self 传参。 self 由解释器自动传参
对象.方法名(参数列表) 本质上就是 => Student.方法名(对象,参数列表)
class Student:
def __init__(self, name, score):
# 方式一
self.name = name
self.score = score
# 实例方法
def say_score(self):
# 方式二
self.age = 21
print(f"{self.name}的分数是:{self.score}")
s1 = Student("hhc", 149)
# 调用实例方法
s1.say_score() # hhc的分数是:149
Student.say_score(s1) # hhc的分数是:149
print(s1.age) # 21
# 方式三
s1.salary = 3000
print(dir(s1))
print(s1.__dict__) # {'name': 'hhc', 'score': 149, 'age': 21, 'salary': 3000}
print(isinstance(s1,Student)) # True
类对象
我们在前面讲的类定义格式中,class 类名:实际上,当解释器执行 class 语句时,就会创建一个类对象
class Student:
pass
print(Student) # <class '__main__.Student'>
print(type(Student)) # <class 'type'>
print(id(Student)) # 2000328626512
s1 = Student
s2 = s1()
print(s2) # <__main__.Student object at 0x000001D1BCEE77F0>
print(type(s2)) # <class '__main__.Student'>
print(id(s2)) # 2000329537520
我们可以看到实际上生成了一个变量名就是类名 Student 的对象。我们通过赋值给新变量 Stu2 ,也能实现相关的调用。说明,确实创建了“类对象”。
类属性
前面我们说实例属性是不共享的,而这个类属性是全部实例对象共享的。它是定义在类里面,函数外面的变量
在类中或者类的外面,我们可以通过: 类名.类变量名 来读写
在类外面,不要通过:对象.类变量名 来写,因为这样会创建一个实例变量,初始值为类变量的值,当进行修改时,改的是实例变量,而不是类属性
class Student:
company = "mjj"
count = 0
def __init__(self, name, age):
self.name = name
self.age = age
Student.count += 1
def say_score(self):
print(f"company:{Student.company}")
print(f"{self.name}的年龄是:{self.age}")
s1 = Student('hhc', 21)
s2 = Student("cg", 22)
print(s1.count) # 2
# 创建了一个实例变量count,初始值为2,进行+1操作,count = 3
s2.count += 1
# Student.count += 1
# s1 没有count实例变量,访问的是类属性
print(s1.count) # 2
# s2 有count这个实例变量,所以访问的是自己的实例变量,为3
print(s2.count) # 3
print(s2.company) # mjj
print(f"共f{Student.count}个对象") # 2
类方法
类方法是从属于“类对象”的方法。类方法通过装饰器 @classmethod 来定义,格式如下:
要点如下:
- @classmethod 必须位于方法上面一行
- 第一个 cls 必须有; cls 指的就是“类对象”本身
- 调用类方法格式: 类名.类方法名(参数列表)。参数列表中,不需要也不能给 cls 传值
- 类方法中访问实例属性和实例方法会导致错误
- 子类继承父类方法时,传入 cls 是子类对象,而非父类对象(⚠️讲完继承再说)
class Student:
company = "mjj"
@classmethod
def printCompany(cls):
print(cls.company)
s1 = Student()
Student.printCompany() # mjj
s1.printCompany() # mjj
静态方法
Python中允许定义与“类对象”无关的方法,称为“静态方法”。 “静态方法”和在模块中定义普通函数没有区别,只不过“静态方法”放到了“类的名字空间里面”,需要通过“类调用”。静态方法中访问实例属性和实例方法会导致错误
class Student:
@staticmethod
def add(num1,num2):
print(num1+num2)
Student.add(30,45) # 75
Student.add("hhc","llxq") # hhcllxq
__ del__方法 (析构函数)和垃圾回收机制
__ del__() 称为“析构方法”,用于实现对象被销毁时所需的操作。当对象没有被引用时,由垃圾回收器调用__ del__()
class Student:
def __del__(self):
print(f"{id(self)}被销毁")
s1 = Student()
s2 = Student()
s3 = Student()
s4 = s3
del s2
del s3
print("程序结束")
"""
2145245607824被销毁
程序结束
2145245608112被销毁
2145245607920被销毁
"""
__ call__ 方法和可调用对象
Python 中,凡是可以将 () 直接应用到自身并执行,都称为可调用对象。对象不是可执行对象,即对象()这种方式是错误的,如果想让对象变成可执行对象,可以给类添加call函数,让对象变成可执行对象,其本质就是执行call方法
class Student:
def __call__(self, *args):
print(*args)
s1 = Student()
s1("hhh","llxq") # hhh llxq
方法的动态性
方法的动态性即在类外部,我们可以动态的为类添加新的方法,或者动态 的修改类的已有的方法。通过类名.方法1=方法2 对已存在的方法1进行修改,对未存在的方法1进行添加,但是方法2中必须有个形参,用来提供self的传递。本质上就是修改引用
class Student:
def read(self):
print("read")
def play(s):
print("play")
def sleep(slef):
print("sleep")
# 将read的引用修改为play的引用
Student.read = play
# 新增一个sleep变量,指向sleep函数
Student.sleep = sleep
s1 = Student()
s1.read() # play
s1.sleep() # sleep
上面的代码可以看到,使用play方法替代了read方法,Student类新增了sleep方法
私有属性和私有方法
python对类的成员没有严格的访问控制,和常量一样,我们只是约定某种形式的变量是私有的:
- 两个下划线开头的属性是私有的(private)。其他为公共的(public)
- 类内部可以访问私有属性(方法)
- 类外部不能直接访问私有属性(方法),但是可以通过 _ 类名__私有属性(方法)名 访问私有属性(方法)
class Student:
__name = "hhc"
def __init__(self, name, age):
self.name = name
self.__age = age
def getAge(self):
print(self.__age)
def __getName(self):
print(self.__name)
print(self.__age)
s1 = Student("hhc",21)
# print(s1.__name) # AttributeError: 'Student' object has no attribute '__name'
#s1.__getName() # AttributeError: 'Student' object has no attribute '__getName'
print(s1._Student__name) # hhc
print(s1._Student__getName()) # hhc 21 None(没有return语句,默认返回None)
print(s1.getAge()) # 21 None
#print(s1.__age) # AttributeError: 'Student' object has no attribute '__age'
print(s1._Student__age) # 21
print(s1.getAge()) # 21 None
print(dir(s1)) # '_Student__age', '_Student__getName', '_Student__name'
property装饰器
@property可以将一个方法的调用方式变成“属性调用”。@property主要用于帮助我们处理属性的读操作、写操作。对于某一个属性,我们可以直接通过:emp1.salary = 30000。如上的操作读操作、写操作。但是,这种做法不安全。比如,我需要限制薪水必须为1-10000的数字。这时候,我们就需要通过使用装饰器 @property来处理。
class Employee:
def __init__(self, name, salary):
self.name = name
self.__salary = salary
@property
def money(self):
print(f"{self.__salary}")
@money.setter
def salary(self,salary):
self.__salary = salary
e1 = Employee("hhc",6200)
e1.salary # 6200
e1.salary = 20000
e1.salary # 20000
None
None 是一个特殊的常量,表示变量没有指向任何对象。在Python中, None 本身实际上也是对象,有自己的类型 NoneType 。你可以将 None 赋值给任何变量,但我们不能创建 NoneType 类型的对象。None和其他类型进行比较,永远返回的是False.
a = None
b = None
print(type(a)) # <class 'NoneType'>
print(type(b)) # <class 'NoneType'>
print(type(None)) # <class 'NoneType'>
print(id(a)) # 140724952571584
print(id(b)) # 140724952571584
print(id(None)) # 140724952571584
print(a == None) # True
print(a == False) # False
对象的三大特性
封装
隐藏对象的属性和实现细节,只对外提供必要的方法。相当于 将“细节封装起来”,只对外暴露“相关调用方法”。 通过前面学习的“私有属性、私有方法”的方式,实现“封装”。
继承
继承让我们更加容易实现类 的扩展。实现代码的重用,如果一个新类继承自一个设计好的类,就直接具备了已有类的特 征,就大大降低了工作难度。已有的类,我们称为“父类或者基 类”,新的类,我们称为“子类或者派生类”。一个类如果没有显式的继承某个类,那么它默认继承的是object类,可以说object是所有类的父类。
子类是继承了父类非构造函数的所有行为和状态,包括私有的。
对于构造函数:
- 如果子类没有重写构造函数,则默认使用父类的构造函数
- 如果子类实现了构造函数,则使用自己的构造函数
- 如果想要使用父类的构造函数,可以使用super()关键字,也可以使用 父类名.init(self, 参数列表)
class Persion:
def __init__(self, name, age):
print("父类的init...")
self.name = name
self.age = age
class Student(Persion):
def __init__(self, name, age, score):
print("子类的init...")
# Persion.__init__(self,name,age)
super(Student,self).__init__(name,age)
# self.name = name
# self.age = age
self.score = score
s1 = Student("hhc", 21, 90)
print(s1.name, s1.age, s1.score)
方法重写
子类继承了父类的方法,当子类和父类有相同的方法,则就是说子类对父类的方法进行了重写,使用该方法时,默认使用子类实现的方法,如果想要使用父类的方法可以使用suoer().方法名() or 父类.方法名(self)
class Persion:
def __init__(self, name, age):
self.name = name
self.age = age
def print_age(self):
print(f"父类方法:age={self.age}")
def print_name(self):
print(f"父:name={self.name}")
class Student(Persion):
def __init__(self, name, age, score):
# Persion.__init__(self,name,age)
super(Student,self).__init__(name,age)
self.score = score
def print_score(self):
print(f"子:score={self.score}")
def print_age(self):
# 使用父类的方法
# super().print_age()
Persion.print_age(self)
print(f"子:score={self.age}")
s1 = Student("hhc", 21, 90)
print(s1.name, s1.age, s1.score)
s1.print_score()
s1.print_age()
查看类的继承层次结构
通过类的方法 mro() 或者类的属性 mro 可以输出这个类的继承层次 结构。
class A:pass
class B(A):pass
class C(B):pass
print(A.mro()) # [<class '__main__.A'>, <class 'object'>]
print(B.mro()) # [<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
print(C.mro()) # [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
print(A.__mro__) # (<class '__main__.A'>, <class 'object'>)
print(B.__mro__) # (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
print(C.__mro__) # (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
重写 __ str__()方法
object有一个 __ str__() 方法,用于返回一个对于“对象的描述”。内置函数str(对象),调用的就是__ str__()
__ str__()经常用于 print() 方法,帮助我们查看对象的信息。 __ str__() 可以重写
class Persion:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
print("重写str方法")
return f"{self.name}的年龄为{self.age}"
p = Persion("hhc", 21)
print(p) # hhc的年龄为21
print(str(p)) # hhc的年龄为21
多重继承
Python支持多重继承,一个子类可以有多个“直接父类”。这样,就 具备了“多个父类”的特点。但是由于,这样会被“类的整体层次”搞的 异常复杂,尽量避免使用。
super获取父类的定义
在子类中,如果想要获得父类的方法时,我们可以通过super()来做。super()代表父类的定义,不是父类对象。super(子类名称,self).init(参数列表)
class Persion:
def __init__(self, name, age):
print("父__init__")
self.name = name
self.age = age
def say_age(self):
print(f"父say_age:{self.age}")
class Student(Persion):
def __init__(self, name, age, score):
# 调用父类的构造方法
super(Student,self).__init__(name, age)
self.score = score
# 重写父类方法
def say_age(self):
# super().say_age()
Persion.say_age(self)
print("子say_age")
s = Student("hhc", 21, 90)
s.say_age()
多态
多态(polymorphism)是指同一个方法调用由于对象不同可能会 产生不同的行为。
比如:现实生活中,同一个方法,具体实现会完全不同。 比 如:同样是调用人“吃饭”的方法,中国人用筷子吃饭,英国人用 刀叉吃饭,印度人用手吃饭。
注意点:
- 多态是方法的多态
- 多态的前提是必须继承和方法的重写
class Animal:
def shut(self):
print("Animal叫了一下")
class Dog(Animal):
def shut(self):
print("Dog汪汪叫")
class Cat(Animal):
def shut(self):
print("Cat喵喵叫")
def animalShut(animal):
animal.shut()
animalShut(Dog())
animalShut(Cat())
特殊方法和运算符重载
Python的运算符实际上是通过调用对象的特殊方法实现的。
a = 30
b = 50
print(a+b) # 80
print(a.__add__(b)) # 80
class Person:
def __init__(self, name):
self.name = name
def __add__(self, other):
if isinstance(other, Person):
return f"{self.name}-{other.name}"
else:
return "不是同类,不能相加"
def __mul__(self, other):
if isinstance(other, int):
return self.name*other
else:
return "不是同类,不能相乘"
p1 = Person("hhc")
p2 = Person("lxq")
print(p1+p2) # hhc-lxq
print(p1+2) # 不是同类,不能相加
print(p2*4) # lxqlxqlxqlxq
特殊属性
Python对象中包含了很多双下划线开始和结束的属性,这些是特殊 属性,有特殊用法。这里我们列出常见的特殊属性:
组合
除了继承,组合也能实现代码的复用,它和继承的区别是,继承是is a 属于关系,比如狗属于动物,学生属于人类;组合是has a 有关系,比如电脑有鼠标,显示器...
class Computer:
def __init__(self, cup, screen):
self.cup = cup
self.screen = screen
class Cpu:
def calculate(self):
print("计算...")
class Screen:
def show(self):
print("显示器显示")
c = Computer(Cpu(),Screen())
c.cup.calculate() # 计算...
c.screen.show() # 显示器显示
设计模式
工厂模式
工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。
class CarFactory:
def createCar(self, brand):
if brand == "奔驰":
return Benz()
elif brand == "宝马":
return BMW()
elif brand == "比亚迪":
return BYD()
else:
return "杂牌"
class Benz:
pass
class BMW:
pass
class BYD:
pass
carFactory = CarFactory()
b = carFactory.createCar("宝马")
print(b,type(b)) # <__main__.BMW object at 0x0000014F11BCB280> <class '__main__.BMW'>
c = carFactory.createCar("比亚迪")
print(c,type(c)) # <__main__.BYD object at 0x0000014F11BCB250> <class '__main__.BYD'>
d = carFactory.createCar("奔驰")
print(d,type(d)) # <__main__.Benz object at 0x0000014F11BCB3D0> <class '__main__.Benz'>
e = carFactory.createCar("hhc")
print(e,type(e)) # 杂牌 <class 'str'>
单例模式
单例模式(Singleton Pattern)的核心作用是确保一个类只有一个 实例,并且提供一个访问该实例的全局访问点。
class MySingleton:
__obj = None
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__obj is None:
cls.__obj = object.__new__(cls)
return cls.__obj
def __init__(self, name):
if self.__init_flag:
print("init...")
self.name = name
self.__init_flag = False
m1 = MySingleton("hhc")
m2 = MySingleton("lxq")
print(m1.name,type(m1),id(m1)) # hhc <class '__main__.MySingleton'> 1847743721776
print(m2.name,type(m2),id(m2)) # hhc <class '__main__.MySingleton'> 1847743721776