在python中万物皆对象
33.面向对象
1.类
- 类的定义:通常在Python中的__init__方法中初始化实例对象的属性,这个方法是一个初始化方法。而使用python默认提供的构造方法__new_
- 定义一个类示例如下:
#定义一个类 class Student: native_place = '吉林' #类里的变量称为类属性 def __init__(self,name,age): self.name = name #name为实例属性 self.age = age #age为实例属性 #实例方法 def instanceFunc(self): print('这是实例方法') #静态方法 @staticmethod def staticFunc(): print('这是静态方法') #类方法 @classmethod def classFunc(cls): print('这是类方法')
- 注意:实例方法的第一个参数默认是self,即实例对象的引用;类方法的第一个参数默认是cls,即类对象的引用。类对象和实例对象之间的关系?参见3
class Student(object): def instanceMethod(self): # 实例方法: <__main__.Student object at 0x00000123CCD1F6A0> print('实例方法:',self) # 类方法 @classmethod def classMethod(cls): # 类方法: <class '__main__.Student'> 1253265361152 print('类方法:',cls,id(cls)) # 静态方法 @staticmethod def staticMethod(): pass s = Student() # 实例对象的引用: <__main__.Student object at 0x00000123CCD1F6A0> print('实例对象的引用:',s) s.instanceMethod() # 类对象的引用: <class '__main__.Student'> 1253265361152 print('类对象的引用:',Student,id(Student)) s.classMethod() # 三种方法存储在类对象的__dict__属性中,没有在实例当中 print(s.__dict__) #{} print(Student.__dict__)
- 类也是对象,属于type类型的
- 可以将类赋值给一个变量
- 可以为它增加属性
- 可以作为实参进行参数的传递
# Student对象由type类创建 class Student(object): pass var = Student # <class '__main__.Student'> <class 'type'> print(var, type(var)) instanceObj = var() # <__main__.Student object at 0x000001D756C5EA70> <class '__main__.Student'> print(instanceObj, type(instanceObj))
- 元类的概念:创建类对象的类(即type类) ,它继承自object类。
- type是一个用来创建类对象的类,使用type类创建类对象示例如下:
# 创建一个类对象 # 第一个参数指定对象名,第二个参数指定基类,第三个参数指定由属性构成的字典 Student = type('Student', (), {}) print(Student) # <class '__main__.Student'> # 创建该类的实例 print(Student())# <__main__.Student object at 0x00000242A2B6EA30> # 使用class关键字定义类,python内部就实现了创建Student这个类对象 class Student: pass print(Student) # <class '__main__.Student'>
- __class__属性:这个属性用于获取对象所属的类型,有了这个属性,可以实现在实例方法中访问类属性
class Student: native_school_name = 'ZTU' def getName(self): return __class__.native_school_name stu = Student() print(stu.getName()) # ZTU
- __metaclass__属性:指定用来创建当前类对象(类也是一个对象)的元类,一般在自定义类中作为属性。
class MMetaClass(type): def __new__(cls, name, bases, attr): print("这是一个构造方法") return super().__new__(cls, name, bases, attr) class Student(metaclass=MMetaClass): pass
- 总结,如下图所示
a = 1024 print(a.__class__) # <class 'int'> print(a.__class__.__class__) # <class 'type'> str = 'test' print(str.__class__) # <class 'str'> print(str.__class__.__class__) # <class 'type'> class Student: pass obj = Student() print(obj.__class__) # <class '__main__.Student'> print(obj.__class__.__class__) # <class 'type'>
- 类的创建流程:
- 检测类中是否明确__metaclass__属性.
- 检测父类中是否存在__mataclass__属性
- 检测模块中是否存在__metaclass__属性
- 通过内置的type这个元类来创建类对象
- 实例对象的创建
#实例对象可以调用实例方法,类方法,静态方法
stu = Student('Jack',20)
stu.staticFunc()
stu.classFunc()
#下面两种调用方式结果一致
stu.instanceFunc() #这是实例方法
# 类对象理解为创建实例对象的模板
Student.instanceFunc(stu)#这是实例方法
print(stu.name)
print(stu.age)
#Student为类对象,和stu实例对象不一样。访问类属性
print(Student.native_place) #吉林
#调用类方法
Student.classFunc() #这是类方法
#调用静态方法
Student.staticFunc() #这是静态方法
# 类的创建方式
# 1.通过元类来创建类对象
def eat(self):
print(self)
dog = type('Dog',(),{'weig':23,'eat':eat})
# 变量dog引用着类对象
print(dog) #<class '__main__.Dog'>
#创建实例对象
d = dog()
print(d) #<__main__.Dog object at 0x0000017CFE1F5BE0>
d.eat() #<__main__.Dog object at 0x0000017CFE1F5BE0>
- 动态绑定实例对象的属性和方法
class Student:
#实例方法
def instanceFunc(self):
print("这是实例方法")
def __init__(self,name,age):
self.name = name
self.age = age
#定义一个函数
def func():
print('这是类外定义的函数')
stu1 = Student('Jack',20)
#给实例对象动态绑定属性
stu1.gender = '男'
print(stu1.name,stu1.age,stu1.gender) #Jack 20 男
# 删除实例属性
del stu1.gender
#给实例对象动态绑定方法
stu1.func = func
stu1.func() #这是类外定义的函数
- 增删改查类对象的属性和方法:
class Student(object):
# 类属性
id = '学生'
def __init__(self):
pass
class Teacher(object):
id = '老师'
if __name__ == '__main__':
# 变量s引用着一个实例对象
s = Student()
# Student这个变量引用着类对象
# 增加类属性
Student.addr = 'xxx'
s.__class__.inter = 'study'
# 访问类属性
print(Student.addr) #xxx
print(Student.inter)#study
#删除类属性
# del Student.id
#del s.__class__.id
print(Student.id) #AttributeError
# 还可以通过变量s访问类属性,实例对象中的__class__对象指向类对象
print(s.addr) #xxx
print(type(s.__class__),id(s.__class__))
print(type(s.__class__),id(Student))
# <class 'type'> 1672459155488
# <class 'type'> 1672459155488
# 更改变量s引用的实例对象的__class__引用的值
s.__class__ = Teacher
print(s.id) #老师
# 类属性被各个实例对象共享
one = Student()
two = Student()
print(one.id) #学生
print(two.id) #学生
Student.id = '老师'
print(one.id) #老师
print(two.id) #老师
# 限制添加类属性
class Student:
# 限制只能再添加两个实例对象属性name,age
__slots__ = ['name','age']
s = Student()
s.name = 'Jack'
s.age = 23
s.id = 2021 #AttributeError
- 设置只读的实例属性
class Student(object):
def __init__(self):
self.__age = 18
# @property的作用是可以以使用属性的方式,来使用这个方法
# 方法名设置为age的目的是防止在类的外部新创建age这个实例属性
@property
def age(self):
return self.__age
s = Student()
print(s.age) # 18
s.age = 23 # AttributeError: can't set attribute
class Student:
# 私有的实例属性带有两个下划线
__addr = "xxx"
# 公有的类属性则不带有两个下划线
name = "zs"
def info(self):
return self.__addr
stu = Student()
# AttributeError: 'Student' object has no attribute '__addr'
print(stu.__addr)
print(stu.info())
print(stu.name)
2.面向对象的三大特征
面向对象的三大特征:封装,继承,多态
- 封装:在python中没有专门的修饰符用于属性私有,如果该属性不希望在类外部被访问,前边使用两个下划线;也没有专门的修饰符用于属性保护,希望受到保护的属性使用一个下划线;不加一个下划线表示公有属性
class Student:
def __init__(self,name,age):
self.name = name
self.set_age(age)
def set_age(self,age):
self.__age = age
def get_age(self):
return self.__age
stu = Student('Jack',23)
# name属性不带下划线,表示共有属性
print(stu.name,stu.get_age()) #Jack 23
print(dir(stu))
print(stu._Student__age) #23,可以通过这种方式获取不希望再类外直接访问的属性__age
#伪私有,将私有属性重命名为‘__类名__属性名’(名字重整)
- 继承:
- 语法格式如下
class 子类名(父类1,父类2,...): pass
- 注:Python中支持多继承
#如果一个类没有继承任何类,则默认继承object类 class Person(object): def __init__(self,name,age): self.name = name self.age = age def info(self): print(self.name,self.age) class Student(Person): def __init__(self,name,age,stu_num): #定义子类时,必须在其初始化函数中调用父类的初始化函数 super().__init__(name,age) self.stu_num = stu_num stu = Student('Jack',23,20170201818) stu.info() #Jack 23
- 多态
class Animal(object):
def eat(self):
print('Animal base calss')
class Dog(Animal):
#重写父类中的方法
def eat(self):
print('Dog吃骨头')
class Cat(Animal):
def eat(self):
print('Cat吃鱼')
class Person(object):
def eat(self):
print('人吃饭')
def func(obj):
obj.eat()
func(Animal()) #Animal base calss
func(Dog()) #Dog吃骨头
func(Cat()) #Cat吃鱼
func(Person()) #人吃饭
- 方法重写
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
def info(self):
print('姓名为:{0},年龄为:{1}'.format(self.name,self.age))
class Student(Person):
def __init__(self,name,age,stu_num):
super().__init__(name,age)
self.stu_num = stu_num
#对继承自父类中的方法进行重写
def info(self):
super().info()
print('学号为:{0}'.format(self.stu_num))
stu = Student('Jack',23,20170201818)
#姓名为:Jack,年龄为:23
#学号为:20170201818
stu.info()
- 内置函数dir()可以查看指定对象的所有属性。object类是所有类的父类,它的_str_()方法用于返回对一个对象的描述,相当于Java中的toString方法,可以重写该方法。
print(stu.__str__()) #<__main__.Student object at 0x0000027FE5CCFFA0>
print(Student.__str__(Student)) #<class '__main__.Student'>
print(dir(stu))
- 类的特殊属性和方法
- 特殊属性:
__dict__
:获取类对象或者实例对象的所有属性和方法的字典
class A: pass class B: pass class C(B,A): def __init__(self,name): self.name = name c = C('张三') print(c.__dict__) #实例对象的属性字典,{'name': '张三'} print(A.__dict__) print(C.__mro__) #类的层次结构
- 特殊方法:
__new__
:用于创建对象__init__
:对创建的对象进行初始化__del__()
方法:当对象被删除时会自动被调用,相当于C++中的析构函数
- 特殊属性:
- 深拷贝和浅拷贝
- 对于深copy来说,列表是在内存中重新创建的,列表中可变的数据类型是重新创建的,列表中的不可变的数据类型是公用的。
- 对于浅copy来说,只是在内存中重新创建了一个空间存放一个新列表,但是新列表中的元素与原列表中的元素是公用的。
#浅拷贝示例(只拷贝第一层) lst1 = [[1,3],'hello','world'] lst2 = lst1.copy() #3013312822528 3013310103344 3013311459952 print(id(lst1[0]),id(lst1[1]),id(lst1[2])) #3013312822528 3013310103344 3013311459952 print(id(lst2[0]),id(lst2[1]),id(lst2[2])) #对lst2中可变序列的修改影响到了lst1 lst2[0][1] = 100 print(lst1) #[[1, 100], 'hello', 'world'] print(lst2) #[[1, 100], 'hello', 'world'] #深拷贝示例(克隆) import copy lst1 = [[1,3],'hello','world'] lst2 = copy.deepcopy(lst1) #2743982477760 2743981360112 2743981109936 print(id(lst1[0]),id(lst1[1]),id(lst1[2])) #2743982477376 2743981360112 2743981109936 print(id(lst2[0]),id(lst2[1]),id(lst2[2])) #对lst2中可变序列(如列表)的修改不会影响lst1 lst2[0][1] = 100 print(lst1) #[[1, 3], 'hello', 'world'] print(lst2) #[[1, 100], 'hello', 'world'] lst2[2] = "hi" print(lst1) #[[1, 3], 'hello', 'world'] print(lst2) #[[1, 100], 'hello', 'hi']