普通视图

Received today — 2026年4月25日技术博客
Received yesterday — 2026年4月24日技术博客
Received before yesterday技术博客

Python中的类和对象

2026年1月20日 00:00

未完待续

1.定义类

python3中的根类,有且只有一个:object(小写)

⚠️大写的Object不存在于python内置中,是自己定义的,为防止混淆不建议使用

__bases__是python中一个内置的变量,代表基类

# 隐式继承,python3自动处理class Cat:    pass# 显式继承class Dog(object):    passif __name__ == '__main__':    print( Cat.__bases__ ) # (<class 'object'>,)    print( Dog.__bases__ ) # (<class 'object'>,)

2.类的成员

2.1 构造方法

  • python初始化类的对象,使用__init__()这个特殊方法作为类的构造方法,创建对象时会被自动调用。
  • python构造方法不支持重载,不能弄多个__init__(),否则最后一个覆盖前面的。

构造方法就是一种实例方法,实例方法第一个参数必须是self代表当前对象

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
  • 实例属性和类属性不同名时,实例属性通过对象.访问,类属性可以通过类.访问或对象.访问
  • 实例属性和类属性同名是合法但是不推荐的,这种情况下,实例和类会各自有一份,对象.访问到的是实例属性,类属性只能通过类.访问

⚠️__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)
initinit18 元宝20 大黄---------------simaple_name0-------------ChinaChinaChina

有时候,一个类存在属性过多,逐一用参数赋值会很复杂,例如:

  • 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, 100liming1610001

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__

⚠️ __双下划线不可访问的底层实现,实际上是将该变量替换成了_类名__私有变量名,通过将某个私有变量名修改为_类名__私有变量名的形式,可以突破访问控制,这种访问方式极不推荐

例:

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.__ageif __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'}lzj191000219

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 + bif __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 + bif __name__ == '__main__':    Stu.classfunc1()    s1 = Stu()    s1.classfunc2()    s1.__class__.classfunc2()
Stubuptclassfunc2classfunc2classfunc2

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,...)

使用Filebeat采集Nginx日志到ES

2026年4月18日 00:00

filebeat是传统elk组件中logstach的升级替代,能够高性能的采集一些中间件的日志到es中,供检索分析。

1.安装filebeat

首先要安装filebeat到nginx所在服务器,因为我的服务器是rocky linux属于redhat系,故这里通过yum安装,先设置安装源

导入GPG-KEY

rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

新建一个elastic.repo文件在/etc/yum.repos.d下,并粘贴安装源地址

vim /etc/yum.repos.d/elastic.repo

[elastic-9.x]name=Elastic repository for 9.x packagesbaseurl=https://artifacts.elastic.co/packages/9.x/yumgpgcheck=1gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearchenabled=1autorefresh=1type=rpm-md

接下来执行安装,直到安装完成

yum install filebeat -y

2.设置nginx和filebeat

首先确认nginx的日志路径和日志格式,一般日志路径默认就是:

  • /var/log/nginx/access.log 常规访问日志
  • /var/log/nginx/error.log 错误日志

在nginx.conf配置文件中,默认的日志格式是:

log_format  main  ' $remote_addr - $remote_user [$time_local] "$request" '                  '$status $body_bytes_sent "$http_referer" '                  '"$http_user_agent" "$http_x_forwarded_for"';

为了区分各个主机的访问记录,我选择增加一个主机的字段:$host

log_format  main  '$host $remote_addr - $remote_user [$time_local] "$request" '                  '$status $body_bytes_sent "$http_referer" '                  '"$http_user_agent" "$http_x_forwarded_for"';

亲测filebeat可以识别上述的日志格式,自动提取有效信息

然后设置filebeat,通过yum安装的filebeat,默认全局配置文件位于/etc/filebeat/filebeat.yml,有这样几项需要修改

output.elasticsearch:  # 改成自己es地址和端口  hosts: ["localhost:9016"]  # 改成自己的索引格式  index: "nginx-logs-%{+yyyy.MM.dd}"  # 通信协议按需要修改  protocol: "http"  # es用户名密码,必须设置  username: "elastic"  password: "***************"# 需要新增这两项,索引数据格式模板名称setup.template.name: "nginx-logs"setup.template.pattern: "nginx-logs-*"

然后对nginx的采集功能进行设置,filebeat支持很多中间件的日志采集,通过yum安装的filebeat,默认的各中间件的采集配置文件位于:/etc/filebeat/modules.d/

首先要将默认的nginx配置文件nginx.yml.disabled复制出一份nginx.yml,因为最后filebeat只会自动导入读取.yml结尾的文件

cp /etc/filebeat/modules.d/nginx.yml.disabled /etc/filebeat/modules.d/nginx.yml

vim编辑/etc/filebeat/modules.d/nginx.yml配置文件,针对nginx的采集进行配置

- module: nginx  # 打开常规访问日志采集,指定日志路径  access:    enabled: true    var.paths: ["/var/log/nginx/access.log"]    var.timezone: "Asia/Shanghai"  # 打开错误日志采集,指定日志路径  error:    enabled: true    var.paths: ["/var/log/nginx/error.log"]    var.timezone: "Asia/Shanghai"

都修改完成后,通过filebeat test config命令,验证配置文件是否有语法错误

[root@VM-0-3-rockylinux ~]# filebeat test configConfig OK

然后启动filebeat,并且能看到进程,启动成功

[root@VM-0-3-rockylinux ~]# systemctl start filebeat[root@VM-0-3-rockylinux ~]# ps -ef | grep filebeatroot      279214       1  0 Apr17 ?        00:00:09 /usr/share/filebeat/bin/filebeat --environment systemd -c /etc/filebeat/filebeat.yml --path.home /usr/share/filebeat --path.config /etc/filebeat --path.data /var/lib/filebeat --path.logs /var/log/filebeatroot      484905  454652  0 14:33 pts/2    00:00:00 grep --color=auto filebeat

3.查看索引

登录kibana,打开开发工具,就能看到filebeat建的索引和采集到的日志了,还可以根据业务需要制作图表等

还可以通过检索,通过链接和访问次数进行聚合,查出一些攻击和刺探的恶意请求,例如:

GET /nginx-logs-2026.04.18/_search{  "size": 0,   "aggs": {    "domain_counts": {      "terms": {        "field": "url.domain",       "size": 20000      },      "aggs": {        "domains_per_ip": {          "terms": {            "field": "source.ip",            "size": 20000                      },          "aggs": {            "domains_per_path": {              "terms": {                "field": "url.path",                "size": 20000                              }            }          }        }      }    }  }}

Chrome浏览器自带翻译的诡异Bug:ID翻译后竟然变化了

当前负责的项目主打海外业务,总免不了和多语言打交道。但最近我在Vite+Vue3+Element Plus技术栈的项目里,遇到了一个堪称“玄学”的bug——Chrome浏览器自带翻译功能,居然能把表格里的数字ID直接改了!从印度同事到国内运...

谷歌广告AdSense/GAM 接入避坑 + 填充监听 + 问题排查

作为长期负责海外业务的开发者,公司业务中经常会和谷歌的人员有沟通。在接入谷歌广告生态时,踩了很多坑,很多坑都是和谷歌的技术人员沟通后才发现问题的(有些最新情况和文档不一致,很坑)。

如何优雅判断 AdBlock 是否开启?前端实现方案分享

因为自己主要负责面对海外的用户项目,发现以前的检测AdBlock/AdBlock Plus开启状态方法已失效了,于是专门研究了一下,并尝试了很多方法。发现npm上的插件基本都失效了,于是研究了一下屏蔽原理。

JavaScript 正则陷阱:全局匹配 /g 导致的 test () 方法异常详解

在 JavaScript 正则表达式使用中,一个看似简单的全局匹配标志/g,可能会在循环检测时引发令人困惑的结果。本文将通过实际案例解析这一现象的底层原因,并提供多种解决方案。

❌