深入理解Python中的类
简介
在Python中,类是实现面向对象编程(OOP)的主要工具。它们允许我们创建自定义的数据结构,它包含了数据和对数据操作的方法。本文将帮助你理解Python中类的工作原理,以及如何有效地使用它们。
类的基本概念
定义一个新式类
在Python中定义一个类使用关键字class
:
class Person(object):
# 定义两个方法
def eat(self,food):
print('吃', food)
def sleep(self,t):
print('每天至少睡',t,'小时')
创建实例
要使用类,我们需要创建它的实例:
实例对象后,会在内存中分配一块空间,这个空间就是实例对象的位置
tom = Person()
实例多个对象
实例多个对象会在内存中分配不同的空间,然后将地址引用给对象名。
a = Person()
b = Person()
c = Person()
print(a)
print(b)
print(c)
'''
输出
<__main__.MyClass object at 0x000002D374234E80>
<__main__.MyClass object at 0x000002D3745783D0>
<__main__.MyClass object at 0x000002D37433A5E0>
'''
当我们创建一个类的实例时,__init__
方法会被自动调用,接受我们传递的参数(在这个例子中是"我是一个实例属性")。
访问属性和方法
创建实例后,我们可以访问其属性和方法:
tom.eat('饭')
tom.eat('海鲜')
tom.sleep(8)
'''
输出
吃 饭
吃 海鲜
每天至少睡 8 小时
'''
私有属性和方法
私有属性
在Python中,私有属性是指在类的内部使用的属性,它不应该被类的外部所访问。Python并没有真正的私有化支持,但是有一种约定可以使属性变得像是私有的,其方式是通过在属性名前加上双下划线(__
)来实现。
定义属性时,没有任何修饰的都是公有属性,如果在属性或方法前,加上两个下划线,那么这个属性或方法,python解释器就会认为是私有的。
class Account(object):
def __init__(self,name,balance):
# 定义了两个公有属性,这两个属性在类的外部也是可以访问的
# self.name = name
# self.balance = balance
# 因为公有的属性破坏程序的封装性,导致数据不安全,所以将属性定义私有的
# 当一个类中的属性或方法,全部是私有时,这个类是无意义的(因为与外界完全隔离了)
self.__name = name
self.__balance = balance
# 私有属性定义好后,可以保证数据 的访问安全
# 但是还有需求去属性进行访问,可以通过公有接口方法进行间接 访问
# 一般对私有属性会提供 一种 称为 存取器方法的公有方法
# set/ get方法
# set_属性名 get_属性名
# 因为帐户名一旦确认后无需修改,所以可以不提供 set 方法
# def get_name(self):
def get_username(self):
return self.__name
# 给余额提供 存取方法
def set_balance(self,new_balance):
if isinstance(new_balance,int):
#isinstance(对象,类型)检查对象是否属于某种类型
self.__balance = new_balance
else:
print('不能存玉皇大帝的钱')
def get_balance(self):
return self.__balance
# 定义一个查看信息的方法
def show_info(self):
# 在类的内部,访问对象的私有属性
print(self.__name + ' 有 ', self.__balance, '元')
jack = Account('JackMa',9999999)
print(jack.get_username())
print(jack.get_balance())
jack.set_balance(88888888)
print(jack.get_balance())
jack.set_balance('十个亿')
money = jack.get_balance()
print(money)
'''
输出
JackMa
9999999
88888888
不能存玉皇大帝的钱
88888888
'''
私有方法
在Python中,私有方法是指那些不应该在类的外部被直接调用的方法。就像私有属性一样,Python通过在方法名前加上双下划线(__
)来实现方法的私有化。这种命名约定会导致Python解释器对方法名进行“名称改写”(name mangling),就像私有属性一样。
私有方法主要用于类的内部逻辑,不应该被类的外部客户代码直接使用。它们通常用于辅助公共方法或被其他私有方法使用。
'''
私有方法的使用
'''
class ThunderBird(object):
# 实现一个初始方法,用来保存下载任务
def __init__(self):
self.__list = []
# 实现一个公有的方法,用来添加任务
def add_task(self,url):
self.__list.append(url)
# 在类的内部,直接 访问私有方法
self.__download_data(url)
# 核心代码 ,用来下载数据的算法
def __download_data(self,url):
print(f'通过地址 {url} 下载数据中。。。。')
# 测试
tb = ThunderBird()
# 通过一个公有方法,间接 访问的了对象的私有方法,下载数据
tb.add_task('http://www.dytt88.net/复联4.mp4')
# 通过地址 http://www.dytt88.net/复联4.mp4 下载数据中。。。。
# 私有方法在类的外部是不能拼接访问的。
# tb.__downoad_data('http://www.dytt88.net/复联4.mp4')
# 会报错AttributeError: 'ThunderBird' object has no attribute '__downoad_data'
继承
Python支持类的继承,允许我们创建新的类作为现有类的子类,继承其属性和方法,并可以添加新的属性和方法或覆盖现有的。
1、当发生继承后,子类会继承父类中的属性和方法,可以直接 使用
# 定义一个父类
class Phone(object):
def __init__(self):
self.name = '电话'
# 定义一个打电话的方法
def call(self,number):
print(f'正在给 { number} 打电话')
# 定义一个子类
class iPhone(Phone):
# 添加一个拍照方法
def carmera(self):
print('正在拍照')
# 当发生继承后,子类会继承父类中的属性和方法,可以直接 使用
iphonex = iPhone()
iphonex.call('13800138000')
iphonex.carmera()
print(iphonex.name)
dgd = Phone()
dgd.call('13800138000')
print(dgd.name)
'''
输出
正在给 13800138000 打电话
正在拍照
电话
正在给 13800138000 打电话
电话
'''
2、子类不能直接使用父类的私有属性和方法,可以通过访问父类的公有方法间接使用父类私有属性和方法。
class Father(object):
def __init__(self):
self.__money = 999
self.name = 'tom'
def __show(self):
print('这个是父类中的一个私有方法')
def display(self):
print('这是一个父类中的公有方法')
self.__show()
# 定义一个子类
class Son(Father):
# 定义一个自己的方法
def play(self):
print('这是子类中玩的方法')
# 在子类中不能直接使用父类中的私有方法
# self.__show()
# 通过继承得到的父类的公有方法,间接 执行父类的私有方法
self.display()
s = Son()
s.play()
s.display()
3、子类初始化父类属性
# 父类
class Father(object):
def __init__(self,name):
print('Father Init Run ...')
self.name = name
# 子类
class Son(Father):
def __init__(self,name, age):
# 因为子类提供了 init 方法后,那么在使用子类实例对象时,
# 就会调用 子类自己 init 方法,
# 那么就不会再调用 父类的init方法了,父类当中的属性就不会有绑定的机会
# 所以这时是没有父类的属性的
# 如果想父类中的属性可以得到,需要执行父类中的init方法
# 父类名.init()
Father.__init__(self, name)
print('Son Init run ..')
self.age = age
# 测试
s = Son('Tom', 12)
print(s.name)
print('age:', s.age)
4、子类重写父类中的方法
# 父类
class Father(object):
# 实现一个治病
def cure(self):
print('父类是个老中医,使用中医古法治病')
class Son(Father):
# 子类也是一个大夫,也有治病的功能
def cure(self):
# 直接使用 父类名.方法名 形式来引用父类的功能
Father.cure(self)
print('子类出国深造,学成以后,使用中西医结合方法治病')
s = Son()
s.cure()
多层继承
class A(object):
def a(self):
print('a function')
class B(A):
def b(self):
print('b function')
class C(B):
# C类中重写了B类中的方法
def b(self):
B.b(self)
print('b in C function')
def c(self):
print('c function')
class D(C):
def d(self):
print('d function')
# 测试
d = D()
d.a()
d.b()
d.c()
d.d()
'''
输出
a function
b function
b in C function
c function
d function
'''
多层继承属性初始化
class A(object):
def __init__(self, a):
self.a = a
class B(A):
def __init__(self,a,b):
A.__init__(self, a)
self.b = b
class C(B):
def __init__(self,a,b,c):
B.__init__(self,a,b)
self.c = c
class D(C):
def __init__(self,a,b,c,d):
C.__init__(self,a,b,c)
self.d = d
# 测试
d = D(1,2,3,4)
print(d.a)
print(d.b)
print(d.c)
print(d.d)
多继承
多层基层:纵向
多继承:横向
# 定义一个父亲类
class Father(object):
def func_fa(self):
print('Father Function ...')
# 定义一个母亲类
class Mother(object):
def func_mo(self):
print('Mother function ...')
# 上面的两个类,有一个共同子类
class Son(Father, Mother):
def play(self):
print('Son play...')
# 测试
s = Son()
s.play()
s.func_fa()
s.func_mo()
'''
输出
Son play...
Father Function ...
Mother function ...
'''
多继承初始化问题
# 在这种 继承关系 上,这个共同的父类 Person 会被初台化多次,这是继承时的问题
lass Person(object):
def __init__(self,aaa):
print('Person Init ...')
self.aaa = aaa
class Father(Person):
def __init__(self,aaa, name):
Person.__init__(self,aaa)
print('Father Init ...')
self.name = name
class Mother(Person):
def __init__(self,aaa, age):
Person.__init__(self,aaa)
print('Mother Init ...')
self.age = age
class Son(Father, Mother):
def __init__(self,aaa, name,age, gender):
Father.__init__(self,aaa, name)
Mother.__init__(self,aaa, age)
print('Son Init ...')
self.gender = gender
# 测试
s = Son(1, 'Tom',12,'男')
print(s.aaa)
print(s.name)
print(s.age)
print(s.gender)
'''
输出
Person Init ...
Father Init ...
Person Init ...
Mother Init ...
Son Init ...
1
Tom
12
男
'''
多继承初始化问题解决
class Person(object):
def __init__(self,aaa):
print('Person Init ...')
self.aaa = aaa
class Father(Person):
def __init__(self,aaa, name,age):
super(Father, self).__init__(aaa,age)
print('Father Init ...')
self.name = name
class Mother(Person):
def __init__(self,aaa, age):
super(Mother, self).__init__(aaa)
print('Mother Init ...')
self.age = age
class Son(Father, Mother):
def __init__(self,aaa, name,age, gender):
# 参数二是当前类的实例对象
# 参数一是当前类名
# 在参数二对象的所属类的mro关系 中找参数一的下一个类进行实始化
super(Son, self).__init__(aaa,name,age)
print('Son Init ...')
self.gender = gender
# 测试
s = Son(1, 'Tom',12,'男')
print(s.aaa)
print(s.name)
print(s.age)
print(s.gender)
多继承传参问题解决
class Person(object):
def __init__(self,aaa):
print('Person Init ...')
self.aaa = aaa
class Father(Person):
def __init__(self,name,*args):
super(Father, self).__init__(*args)
print('Father Init ...')
self.name = name
class Mother(Person):
def __init__(self,age,*args):
super(Mother, self).__init__(*args)
print('Mother Init ...')
self.age = age
class Son(Father, Mother):
def __init__(self,gender,name,age,aaa):
# 参数二是当前类的实例对象
# 参数一是当前类名
# 在参数二对象的所属类的mro关系 中找参数一的下一个类进行实始化
super(Son, self).__init__(name,age,aaa)
print('Son Init ...')
self.gender = gender
# 测试
# s = Son(1, 'Tom',12,'男')
s = Son('男','Tom',12,1)
print(s.aaa)
print(s.name)
print(s.age)
print(s.gender)
使用Super简化格式
class Person(object):
def __init__(self,aaa):
print('Person Init ...')
self.aaa = aaa
class Father(Person):
def __init__(self,name,*args):
# super(Father, self).__init__(*args)
super().__init__(*args)
print('Father Init ...')
self.name = name
class Mother(Person):
def __init__(self,age,*args):
# super(Mother, self).__init__(*args)
super().__init__(*args)
print('Mother Init ...')
self.age = age
class Son(Father, Mother):
def __init__(self,gender,name,age,aaa):
# 参数二是当前类的实例对象
# 参数一是当前类名
# 在参数二对象的所属类的mro关系 中找参数一的下一个类进行实始化
# super(Son, self).__init__(name,age,aaa)
super().__init__(name,age,aaa)
print('Son Init ...')
self.gender = gender
# 测试
# s = Son(1, 'Tom',12,'男')
s = Son('男','Tom',12,1)
print(s.aaa)
print(s.name)
print(s.age)
print(s.gender)
类的继承书写顺序会影响MRO顺序
class A(object):
pass
class B(A):
pass
class C(A):
pass
# class D(B,C):
# (<class '__main__.D'>, < class '__main__.B' >, < class '__main__.C' >, < class '__main__.A' >, < class 'object' > )
class D(C,B):
# (<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
pass
多重继承时,方法查找顺序遵循MRO顺序
class A(object):
def show(self):
print('A _ Show Run ...')
def info(self):
print('A - Info run ...')
class B(A):
def show(self):
print('B _ Show Run ...')
class C(A):
def show(self):
print('C _ Show Run ...')
def info(self):
super().info()
A.info(self)
print('C - Info run ...')
class D(B,C):
def show(self):
super().show()
A.show(self)
print('D _ Show Run ...')
d = D()
d.show()
d.info()
'''
输出
B _ Show Run ...
A _ Show Run ...
D _ Show Run ...
A - Info run ...
A - Info run ...
C - Info run ...
'''
多态性
多态性是OOP的一个核心概念,它允许我们编写可以与多种类型的对象一起工作的函数或方法,只要这些对象都有一个公共的接口(比如相同名称的方法)。
python中多态不考虑对象中具体类型的情况下使用对象中的方法。
class Father(object):
def cure(self):
print('老中医使用中医治病')
class Son(Father):
def cure(self):
print('小大夫使用中西医结合治病')
class Dog(object):
def bark(self):
print('Won won ...')
class AnimalDoctor(object):
def cure(self):
print('我是个兽医,主要给动物治病,但是也能给人凑合治一下')
# 病人类
class Person(object):
# 需要一个大夫给他治病
def need_doctor(self,doctor):
doctor.cure()
# 测试
p = Person()
p.need_doctor(Father())
p.need_doctor(Son())
# p.need_doctor(Dog())
p.need_doctor(AnimalDoctor())
'''
输出
老中医使用中医治病
小大夫使用中西医结合治病
我是个兽医,主要给动物治病,但是也能给人凑合治一下
'''
类和实例属性
类属性与实例属性之间有重要的区别。类属性是属于类的,被所有实例共享。而实例属性仅属于特定的实例。
实例属性和方法
class Cat(object):
def __init__(self,name):
# 定义一个实例 属性
self.name = name
# 定义了一个实例方法
def info(self):
print(self.name)
def show(self):
print('Show Run :')
self.info()
# 测试
tom = Cat('Tom')
# 使用实例属性
print(tom.name)
# 使用实例方法
tom.info()
tom.show()
# Python中万物皆对象
# print(Cat.name)
Cat('tom').show()
# 结论:
# 为什么类名不能调用 实例 对象属性和实例对象方法呢?
# 因为类对象存在时,不一定有实例对象存在
# 实例属性和实例 方法只能由实例对象调用
类对象和属性
class ClassRoom(object):
# 当定义一个类属性时,相当于这个类中的全局变量
# 该类的所有对象都 可以使用该 类属性
# 可以在所有的对象间进行数据共享
center_kong_tiao = '格力空调'
# 实例方法
def show(self):
print('实例方法中去访问类属性:')
print(ClassRoom.center_kong_tiao)
cr901 = ClassRoom()
print(cr901.center_kong_tiao)
cr902 = ClassRoom()
print(cr902.center_kong_tiao)
cr903 = ClassRoom()
print(cr903.center_kong_tiao)
# cr901.center_kong_tiao = '海尔空调'
ClassRoom.center_kong_tiao = '海尔空调'
print(cr901.center_kong_tiao)
print(cr902.center_kong_tiao)
print(cr903.center_kong_tiao)
cr901.show()
魔法方法
在Python中,有许多内置的方法名有特殊的意义,这些方法名通常以双下划线(__
)开始和结束。它们也被称为魔法方法或特殊方法。这些方法可以让我们重载或者定义对象的操作。
__init__() 方法:
在Python中,__init__方法是一个特殊的方法(有时称为‘魔法方法’),在创建类实例对象时被自动调用。
作用:
初始化实例的属性(起到一个传递参数的作用)
class Myclass(object):
def __init__(self,value):
self.my_attribute = value
def my_method(self):
return self.my_attribute
# 创建一个类的实例
my_instance = MyClass(10)
# 调用实例的方法
print(my_instance.my_method()) # 输出: 10
__init__方法特点:
1、__init__方法不是一个构造函数,它在类实例化对象后运行,用来初始化实例对象属性;
2、第一个参数self是实例对象的引用,确保在方法内部访问实例对象的属性和其他方法
self
不是Python的关键字或强制性的,它只是一个按照惯例使用的变量名。你可以使用其他变量名,但强烈建议遵循这个约定,因为它对于其他Python程序员来说是最易读和理解的。
3、__init__方法可以有任意数量的参数,这些参数在创建类实例对象时传入。
4、__init__方法可以是空的,也可以不写但通常被用来设置实例的初始化状态(传参)。
5、如果你有一个继承链,__init__
方法需要调用基类的 __init__
方法,以确保基类也被正确初始化。这可以通过使用 super()
函数实现。
__str__()方法
在Python中,__str__()
方法是一个特殊的方法,用于定义一个对象的“正式”字符串表示,当你使用print()
函数或str()
函数将对象转换为字符串时,Python解释器会调用这个方法。它通常应该返回一个友好易读的字符串,让用户能够理解对象的状态。
作用:
格式化字符串
class Cat(object):
def __init__(self,name, age, height):
self.username = name
self.age = age
self.height = height
def __str__(self):
print('String Run ..',self.username)
# print(self.username, self.age, self.height)
s = f'姓名:{self.username}年龄:{self.age}身高:{self.height}'
return s
tom = Cat('Tom',1,'50cm')
print(tom)
'''
输出
姓名:Tom年龄:1身高:50cm
'''
_str__()
方法特点:
1、_str__()
方法必须有一个返回值而且返回值必须是字符串。
2、如果需要将实例对象的信息按照一定的格式进行显示,可以在这里进行修饰。
3、修饰完,可以将这个格式化字符串返回,让str()方法执行时,得到该对象转换后的字符串类型。
__del__()方法
在Python中,__del__
方法是一个特殊方法,它被称为析构器(destructor)。这个方法在对象即将被删除时被自动调用。通常情况下,这发生在Python的垃圾回收器决定从内存中删除一个对象时,或者当对象的引用计数降到0时。
__del__
方法的主要用途是确保对象释放分配给它的资源。例如,如果对象持有文件、网络连接或数据库连接等资源,__del__
方法可以确保这些资源在对象不再使用时被正确关闭。
class Cat(object):
def __init__(self,name):
self.name = name
def __del__(self):
# 要在这个方法中将当前对象持有的其它对象手动销毁
del self.name
print('del run ...')
tom = Cat('tom')
# 执行下面的代码 时,会自动 调用 __del__ 方法,将这个对象销毁,回收对象内存资源
del tom
print('over')
__dict__ 是一个魔法属性,用来保存当前对象的所有的成员
结论
Python中的类是创建自定义数据结构的强大工具,它支持继承、多态性和封装等OOP特性。通过理解类如何工作,你可以写出更加模块化、可重用和易于维护的代码。记住,类应该简单而且一目了然,最好每个类只负责一件事情。掌握了Python中的类,将有助于你在面向对象编程的世界中进行更深入的探索。