惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
Kaspersky official blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
AI
AI
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
Scott Helme
Scott Helme
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Engineering at Meta
Engineering at Meta
博客园 - 叶小钗
The GitHub Blog
The GitHub Blog
Microsoft Azure Blog
Microsoft Azure Blog
N
News and Events Feed by Topic
Cloudbric
Cloudbric
B
Blog
Cisco Talos Blog
Cisco Talos Blog
V
Vulnerabilities – Threatpost
N
News and Events Feed by Topic
V
Visual Studio Blog
A
Arctic Wolf
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
U
Unit 42
S
Security @ Cisco Blogs
博客园 - 聂微东
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
Y
Y Combinator Blog
G
GRAHAM CLULEY
L
LINUX DO - 热门话题
量子位
NISL@THU
NISL@THU
Webroot Blog
Webroot Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Troy Hunt's Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tenable Blog
月光博客
月光博客
S
Security Affairs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The Hacker News
The Hacker News
Spread Privacy
Spread Privacy
D
Docker
www.infosecurity-magazine.com
www.infosecurity-magazine.com
雷峰网
雷峰网
博客园 - 司徒正美
T
The Exploit Database - CXSecurity.com
Hugging Face - Blog
Hugging Face - Blog
Help Net Security
Help Net Security
D
DataBreaches.Net

Liu Zijian's Blog | 一个技术博客

使用Certbot自动续签HTTPS证书 使用Filebeat采集Nginx日志到ES Python的协程 Python中的异常 Python的函数 Python的数据结构,推导式、迭代器和生成器 Spring AI集成多模态模型 LangChain4j多模态 LangChain Tools工具使用 Python中的模块和包 Python全局环境和虚拟环境(venv) LangChain Prompt提示词工程 LangChain4j Tools工具使用 基于Dify搭建AI智能体应用 LangChain4j RAG检索增强生成 Spring AI实现MCP Server Spring AI集成MCP Client LangChain4j Prompt提示词工程 Spring AI使用知识库增强对话功能 Spring AI实现一个智能客服 Spring AI实现一个简单的对话机器人 实现MinIO数据的每日备份 自己实现一个DNS服务 简单理解AI智能体 大模型和大模型应用 LangChain开篇 LangChain4j开篇 一个解析Excel2007的POI工具类 DataPermissionInterceptor源码解读 TenantLineInnerInterceptor源码解读 BaseMultiTableInnerInterceptor源码解读 Spring AI开篇 SQL解析工具JSQLParser 芋道源码解读之多租户 芋道源码解读之数据权限 芋道源码解读开篇 Java实现将数据导出为Word文档 OA系统的天数该怎样计算 安装MySQL8 安装MySQL5.7 RockyLinux9环境下编译MySQL8 MySQL字符集及底层原理 Java实现LDAP登录 Docker Compose IPv4和IPv6 使用虚拟机安装一个K8s集群 使用GraalVM原生编译打包SpringBoot工程 Nginx防止目录穿越 Java线程的状态 Nginx防盗链设置 使用python将excel表格转换为SQL INSERT Redis的公共操作命令 Redis数据结构之Bitfleid Redis数据结构之Bitmap Redis数据结构之GEO Redis数据结构之Hash Redis数据结构之HyperLogLog Redis数据结构之List Redis数据结构之Set Redis数据结构之Stream Redis数据结构之String Redis数据结构之ZSet 使用python压缩图片 利用Python实现Hexo站点的持续集成 Nginx设置HTTPS监听 firewalld防火墙工具的使用 Linux信号(signal)机制 MySQL5.7x 主从复制 用IP自签发一个HTTPS证书 基于Hexo实现一个静态的个人博客 RockyLinux9环境下编译MySQL5.7 Docker离线安装 MySQL数据定义语言 Docker与联合文件系统 Docker的网络 Docker的镜像操作 MySQL存储过程 MyBatis-Plus开篇 MySQL变量 MySQL视图 MySQL事务 MySQL插入修改和删除 MySQL查询 MySQL系统命令 Docker的容器操作 Docker的安装和配置 Docker容器数据卷 浅谈OAuth2.0授权原理 JVM开篇 浅谈Linux(Unix)的I/O模型 一个通用的CloseableHttpClient工厂类 JUC可重入锁ReentrantLock JUC读写锁ReadWriteLock Java的单例 Java泛型 Java8的新特性 最近最少使用算法(LRU) MySQL函数 SpringBoot配置和启动 volatile作用分析
Python中的类和对象
Liu Zijian · 2026-01-20 · via Liu Zijian's Blog | 一个技术博客

未完待续

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
10001

python是一门灵活的语言!解决这个问题,可以动态的给某个对象设置成员变量,例如为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
19

2.4 静态方法

上面例子里面出现在类中,参数以self开头的方法,都是实例方法,在python中,还存在静态方法和类方法

在python中,有一种方法,只是出现在类中,但是不依赖于实例和类的普通方法,叫做静态方法,采用@staticmethod装饰器装饰,通常都是作为一种工具方法,除非传入否则不能访问类和实例的成员,可以使用实例名或类名调用,但是参数列表中不能出现selfcls

例: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) #34

2.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) # True

3.对象初始化

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)