Python中的类和对象
本文最后更新于 2026年5月6日
未完待续
1.定义类
python3中的根类,有且只有一个:object(小写)
⚠️大写的Object不存在于python内置中,是自己定义的,为防止混淆不建议使用
__bases__是python中一个内置的变量,代表基类
# 隐式继承,python3自动处理
class Cat:
pass
# 显式继承
class Dog(object):
pass
if __name__ == '__main__':
print( Cat.__bases__ ) # (<class 'object'>,)
print( Dog.__bases__ ) # (<class 'object'>,)2.类的成员
2.1 构造方法
- python初始化类的对象,使用
__init__()这个特殊方法作为类的构造方法,创建对象时会被自动调用。 - python构造方法不支持重载,不能弄多个
__init__(),否则最后一个覆盖前面的。
构造方法就是一种实例方法
class Cat:
'我是说明文档'
def __init__(self):
print('init')
print(self)
print(self.__class__)
if __name__ == '__main__':
cat = Cat()
print(cat)init
<__main__.Cat object at 0x000001982BE1D400>
<class '__main__.Cat'>
<__main__.Cat object at 0x000001982BE1D400>2.2 类和实例的变量
- 实例变量在构造器
__init__中声明,__init__()中通过self.定义实例变量 - 定义在类以内,
__init__()以外的变量是类变量,类似Java中的static - 实例变量和类变量不同名时,实例变量通过
对象.访问,类变量可以通过类.访问或对象.访问 - 实例变量和类变量同名是合法但是不推荐的,这种情况下,实例和类会各自有一份,
对象.访问到的是实例变量,类变量只能通过类.访问
python的实例方法第一个参数必须是当前调用对象(类似Java中的this),名字可以任意取,但是通常都起名叫
self代表当前调用对象
⚠️
__init__()外面的同名变量前面没有static且和self.后面的变量名重名时,self.极易被Java程序员误判为是在为外面的变量初始化或赋值,这种Java的思路,在python中是错误的
例:
__init__()里面的重名name,age是实例变量,通过对象.访问__init__()外面的重名name,age是类变量,通过类.访问country变量不重名,可通过对象.访问,也可以通过类.访问
class Stu:
name = 'simaple_name'
age = 0
country = 'China'
def __init__(self, age, name):
print('init')
self.age = age
self.name = name
def speak(self):
print(self.age, self.name)
if __name__ == '__main__':
stu1 = Stu(18, '元宝')
stu2 = Stu(20, '大黄')
stu1.speak()
stu2.speak()
print('---------------')
print(Stu.name)
print(Stu.age)
print('-------------')
print(stu1.country)
print(stu2.country)
print(Stu.country)init
init
18 元宝
20 大黄
---------------
simaple_name
0
-------------
China
China
China有时候,一个类存在变量过多,逐一用参数赋值会很复杂,例如:
def __str__(self)同样是一个内置函数,类似Java中的toString(),打印对象时转换为字符串- 双下划线
__开头的变量,属于私有变量,类外不可直接访问
class Stu:
def __init__(self, name, age, no, score):
self.name = name
self.age = age
self.no = no
self.__score = score
def __str__(self):
return f'{self.name}, {self.age}, {self.no}, {self.__score}'
if __name__ == '__main__':
stu1 = Stu('liming', 16, 10001, 100)
print(stu1)
print(stu1.name)
print(stu1.age)
print(stu1.no)
#print(stu1.__score) ❌错误 AttributeError: 'Stu' object has no attribute '__score'
liming, 16, 10001, 100
liming
16
10001python是一门灵活的语言!解决这个问题,可以动态的给某个对象设置成员变量,例如为stu2动态指定一个变量no
self.__dict__可以代表对象所有变量
class Stu:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.__score = score
def __str__(self):
return f'{self.__dict__}'
if __name__ == '__main__':
stu1 = Stu('liming', 16, 100)
print(stu1)
stu2 = Stu('liming', 16, 100)
stu2.no = '10002'
print(stu2){'name': 'liming', 'age': 16, '_Stu__score': 100}
{'name': 'liming', 'age': 16, '_Stu__score': 100, 'no': '10002'}上面的写法不推荐,可以采用定义好再去赋值的写法
class Stu:
def __init__(self):
self.name = None
self.age = None
self.no = None
def __str__(self):
return f'{self.__dict__}'
if __name__ == '__main__':
stu1 = Stu()
print(stu1)
stu2 = Stu()
stu2.age = 12
stu2.no = '123'
stu2.name = 'sxh'
print(stu2){'name': None, 'age': None, 'no': None}
{'name': 'sxh', 'age': 12, 'no': '123'}还可以通过可变参数
class Stu:
def __init__(self, **kwargs):
self.name = kwargs.get('name', None)
self.age = kwargs.get('age', None)
self.no = kwargs.get('no', None)
def __str__(self):
return f'{self.__dict__}'
if __name__ == '__main__':
stu1 = Stu(name='lzj', age=19, no='10002')
print(stu1)
stu2 = Stu(name='xiaohong', age=19 )
print(stu2){'name': 'lzj', 'age': 19, 'no': '10002'}
{'name': 'xiaohong', 'age': 19, 'no': None}2.3 成员访问限制
在python中,属性可以进行访问权限控制,和Java类似,如果要访问受保护的属性,就定义方法,这是类封装性的体现
- 受保护的属性
_单个下划线开头,仅仅起到提醒作用,但是不阻止访问,例如_age - 私有属性
__双下划线开头,类外不可访问,否则程序运行出错,例如__age - 公开访问属性 没有下划线的,无限制
- 魔法属性 __双下划线开头和结尾,是python内置的,有特殊的含义,不可自行定义,例如
__dict__
⚠️ python处理
__双下划线的底层实现,是将该变量替换成了_类名__私有变量名,通过将某个私有变量名修改为_类名__私有变量名的形式,可以突破访问控制,这种访问方式极不推荐
例:
class Stu:
def __init__(self, **kwargs):
self.name = kwargs.get('name', None)
self.__age = kwargs.get('age', None)
self._no = kwargs.get('no', None)
def __str__(self):
return f'{self.__dict__}'
def getAge(self):
return self.__age
if __name__ == '__main__':
stu1 = Stu(name='lzj', age=19, no='10002')
print(stu1)
print(stu1.name)
print(stu1.getAge())
print(stu1._no) #不建议的
print(stu1._Stu__age) #不建议的
#print(stu1.__age) #运行出错{'name': 'lzj', '_Stu__age': 19, '_no': '10002'}
lzj
19
10002
192.4 静态方法
上面例子里面出现在类中,参数以self开头的方法,都是实例方法,在python中,还存在静态方法和类方法
在python中,有一种方法,只是出现在类中,但是不依赖于实例和类的普通方法,叫做静态方法,采用@staticmethod装饰器装饰,通常都是作为一种工具方法,除非传入否则不能访问类和实例的成员,可以使用实例名或类名调用,但是参数列表中不能出现self或cls
例:def sum(a, b)就是静态方法
class Stu:
def __init__(self):
name = None
def call(self):
self.sum(1, 2)
self.__class__.sum(3, 4)
Stu.sum(5, 6)
@staticmethod
def sum(a, b):
return a + b
if __name__ == '__main__':
stu1 = Stu()
stu1.call()
stu1.sum(7, 8)
Stu.sum(9, 10)
2.5 类方法
类方法类似Java中的static方法,python的类方法声明时第一个形参永远是cls代表类本身(不是实例),同时除__new__()以外,所有类方法都要采用@classmethod函数装饰器修饰。
python的类方法既能通过类也能通过实例调用,但是为防混淆不建议用实例调用。
主要用于操作类属性或实现工厂模式。
可直接访问类属性,类方法和静态方法,不能直接操作实例属性或实例方法。
例:
class Stu:
school = 'bupt'
def __init__(self):
name = None
def call(self):
pass
@classmethod
def classfunc1(cls):
print(cls.__name__)
print(cls.school)
cls.sum(1, 2)
cls.classfunc2()
@classmethod
def classfunc2(cls):
print('classfunc2')
@staticmethod
def sum(a, b):
return a + b
if __name__ == '__main__':
Stu.classfunc1()
s1 = Stu()
s1.classfunc2()
s1.__class__.classfunc2()Stu
bupt
classfunc2
classfunc2
classfunc2还可以实现工厂方法,例如:
class Person:
@classmethod
def create(cls, name):
return cls(**{'name': name})
def __init__(self, name):
self.name = name
if __name__ == '__main__':
p = Person.create('lsj')
print(p.name)2.6 类属性@property
python中,可以定义类属性,类属性是被@property修饰的一种成员,对外表现是变量,对内实现是方法
类属性本身是类的,但是用于实例,可以实现更好的封装
例:
class Stu:
@property
def age(self):
return self.__age
def __init__(self, age):
self.__age = age
pass
if __name__ == '__main__':
print(Stu.age) #<property object at 0x000001D82AA20D10>
s1 = Stu(29)
print(s1.age) # 29
s2 = Stu(34)
print(s2.age) #342.7 其他魔术方法
__call__()让实例对象被当作函数调用
class Person: def __init__(self, name): self.name = name def __call__(self, age): self.age = age return self if __name__ == '__main__': p = Person('lsj') q = p(22) print(q.name) #lsj print(q.age) #22__repr__()和
__str__()类似,同时定义时,__str__()优先
2.8 @dataclass装饰器
@dataclass的用途类似于Java中的Lombok。
@dataclass可以帮我们省略掉__init__(),__eq__(),__repr__()等常见方法的手写代码,专注于业务逻辑,@dataclass中写的变量名:类型的结构会自动转换为实例变量,自动添加到__init__()中,只有变量名不加类型的默认为类变量。
@dataclass既能声明带默认值的变量,也能生成不带默认值(= None)的变量,无默认值的在前,带默认值的在后。
⚠️
@dataclass装饰器会自动生成__init__(),一旦手写了__init__()会导致装饰器失效,所有自动功能全部关闭,如果想用了装饰器又要加自定义的初始化逻辑,用__post_init__(self, ...)避免冲突
例:
from dataclasses import dataclass
from datetime import date, datetime
@dataclass
class Book:
# 类变量:所有书籍实例对象共享
publisher = "机械工业出版社"
id: int = None
bookName: str = None
price: float = None
author: str = None
course_date: date = None
start_time: datetime = None
# 初始化后校验价格合法性
def __post_init__(self):
print('__post_init__')
if self.price is not None and self.price < 0:
raise ValueError("书籍价格不能为负数!")
if __name__ == '__main__':
print(Book.publisher)
book = Book(id = 1, bookName='疯狂python讲义', price=2.3)
print(book)
book = Book(id = 1, bookName='疯狂python讲义', price=-2.3)机械工业出版社
__post_init__
Book(id=1, bookName='疯狂python讲义', price=2.3, author=None, course_date=None, start_time=None)
__post_init__
Traceback (most recent call last):
File "D:\PycharmProjects\python-lang-test\clazz\t3.py", line 27, in <module>
book = Book(id = 1, bookName='疯狂python讲义', price=-2.3)
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<string>", line 9, in __init__
File "D:\PycharmProjects\python-lang-test\clazz\t3.py", line 21, in __post_init__
raise ValueError("书籍价格不能为负数!")
ValueError: 书籍价格不能为负数!对__eq__()的重写,也会导致比较对象时,比较的是成员变量的值,而不是地址
if __name__ == '__main__':
book1 = Book(id = 1, bookName='疯狂python讲义', price=2.3)
book2 = Book(id = 1, bookName='疯狂python讲义', price=2.3)
print(book1 == book2) # True3.对象初始化
python对象初始化o = Obj(),会经历以下几个过程:
1.调用该Obj类的__new__(cls,...)方法,在内存中开辟空间,创建一个空的实例对象
2.__new__(cls,...)执行完成,必须返回一个当前Obj类的实例对象,就是后续的self
3.python自动把返回的对象,传给__init__(self,...)的第一个参数self
4.调用__init__(self,...)给这个空对象绑定属性,初始化数据
5.返回初始化完成的实例对象给变量o
其中:
__new__(cls,...)是类方法,但是无需显式添加@classmethod装饰器__new__(cls,...)必须有返回值,返回当前类对象时执行__init__(self,...),返回None则跳过__init__(self,...)__new__(cls,...)的时机一定早于__init__(self,...),先创建再初始化- 永远不要出现多个生效的
__init__(self,...)
例:
class Person:
def __new__(cls, name):
obj = super().__new__(cls) #调用父类(直到object)的__new__方法开内存空间,
print('new')
return obj
def __init__(self, name):
print('init')
self.name = name
if __name__ == '__main__':
p = Person('liming')
print(p.name)"如果文章对您有帮助,可以请作者喝杯咖啡吗?"
微信支付
支付宝