阅读视图

Python中的异常

未完待续

1.错误、异常和断言

  • 错误 语法错误,语法检查都过不去
  • 异常 程序语法是正确的,运行到一些地方,也可能会出现错误,运行时检测到的错误就称之为异常
  • 断言 用于判断一个表达式,在表达式条件为false的时候,触发异常

2.异常处理

java中的异常处理包含:try => catch => finally几个步骤,python中的异常处理则是:try => except => else => finally

2.1 try/except

except捕获异常

try:    i = 1 / 0    print("正常执行")except:    print("发生异常了") #发生异常了

2.2 try/except/else

try内没有异常,else块内代码才会执行,else必须放在所有except的后面

__doc__ 是 Python 中每个对象都可能具有的属性,用于存储该对象的文档字符串

try:    result = 3 / 0except ZeroDivisionError as e:    # 打印:异常名 + 异常描述    print(f"1.异常名:{type(e).__name__},异常信息:{e.__doc__}")except (RuntimeError, TypeError, NameError) as e:    print(f"2.异常名:{type(e).__name__},异常信息:{e}")except:    print("Unexpected error")else:    # try内无异常时执行的固定逻辑    print("try内无异常时执行的固定逻辑")    print(result)
1.异常名:ZeroDivisionError,异常信息:Second argument to a division or modulo operation was zero.

如果改为result = 3 / 1,则输出

try内无异常时执行的固定逻辑3.0

2.3 try/except/finally

无论try内是否出现异常,异常是否被捕获,finally块内语句都会执行

try:    result = 3 / 0except ZeroDivisionError as e:    print(f"异常名:{type(e).__name__},异常信息:{e.__doc__}")else:    print(result)finally:    print("finally")
异常名:ZeroDivisionError,异常信息:Second argument to a division or modulo operation was zero.finally

如果改为result = 3 / 1,则输出

3.0finally

⚠️ finally一定会执行,所以finally中要谨慎访问可能因异常导致不存在的变量

try:   result = 3 / 0except ZeroDivisionError as e:   print(f"异常名:{type(e).__name__},异常信息:{e.__doc__}")else:   print(result)finally:   print(result) # ❌ NameError: name 'result' is not defined   print("finally")

2.4 总结

  • try 执行可能产生异常的代码
  • except 发生异常时执行
  • else 没有发生异常时执行
  • finally 有没有异常都会执行

3.异常的产生

3.1 raise抛出

def int_add(x, y):    if isinstance(x, int) and isinstance(y, int):        return x + y    else:        raise TypeError("参数类型错误")print(int_add(1, 2))  # 3print(int_add("1", "2")) # TypeError: 参数类型错误

3.2 assert断言

语法:assert [表达式], "异常信息",表达式一旦我们的断言的预期不符,自动抛出AssertionError

def divide(a, b):    assert b != 0, "断言触发,除数不能为0"    return a / bprint(divide(10, 2))  # 5.0print(divide(10, 0)) # AssertionError: 断言触发,除数不能为0

assert本质上就是简化版的raise,多用于日常开发测试,在生产环境中,应当使用raise,可以通过执行带参数的python -O ***.py命令,屏蔽掉代码中全部的assert语句,直接跳过,不判断不抛异常

4.自定义异常

继承Exception自定义异常,自定义一个属性value

__init__(self, value)会覆盖掉父类Exception的__init__()

class MyError(Exception):    """我是异常doc"""    def __init__(self, value):        self.value = value    def __str__(self):        return repr(self.value)if __name__ == '__main__':    try:        raise MyError('fuck')    except MyError as e:        print(f"触发自定义异常:{type(e).__name__},异常描述:{e.__doc__},异常信息:{e.value}")
触发自定义异常:MyError,异常描述:我是异常doc,异常信息:fuck
  •  

产品,还是玩具? — Baby Press(缝合怪)

这算是给这个东西写的第二篇正式的文章,本来我的想法很简单,做一个简单的前后端分离的系统来完全替代wp的php渲染机制。

只是,在开发的过程中为了迎合wp的各种现有数据格式、插件、主题、shortcode等等,代码复杂度也在不断的提高。得益于ai的崛起,现在生成代码是真的简单方便,原来数个人的工作,现在一人就可以完成了。尽管哪怕没有ai,我自己也能全部搞定。ai在某些方便还是提高了输出效率,原本很多人不是全栈的,现在也给搞成了全干工程师,哪怕不会,也得硬着头皮上,去验证ai写的各种代码。

我一般不喜欢给ai太具体的描述,但是会给一个准确的描述,实现方法,实现路径,实现目标,所以多数时候ai呈现的代码质量尚可。然而,等到实际上线的时候发现还是一堆问题。

做完准备把wp的前端全部迁移到现在的baby press的前端,尝试部署之后出现了一系列问题,当然很多问题源自于测试不充分。为了解决两个系统的整合问题,需要大量的配置文件和代码。除了openresty的配置文件,前后端也生成了一堆默认的配置模板,当然,这些模板主要是为了提供一些自定义的功能,以及安全性提升加密等等。

这么复杂的系统,现在我觉得更像一个玩具,而不是产品,好的产品应该是简单易用,开箱可用的。

DJANGO_SECRET_KEY=dev-secret-key-change-me
DJANGO_DEBUG=1
DJANGO_ALLOWED_HOSTS=127.0.0.1,localhost
# 浏览器里「页面」的 origin(协议+域名+端口),须与前端访问地址一致;逗号分隔、勿加路径。
# 生产示例(Vue 部署在 i 子域、API 在 api 子域时,必须把 i 子域写进来,否则会 CORS 失败):
# CORS_ALLOWED_ORIGINS=http://127.0.0.1:5173,http://localhost:5173,http://i.zhongxiaojie.cn,https://i.zhongxiaojie.cn
CORS_ALLOWED_ORIGINS=http://127.0.0.1:5173,http://localhost:5173
# Django CSRF 信任来源(协议+域名+端口,逗号分隔;用于 /admin/login/ 等表单提交)
# 生产示例:CSRF_TRUSTED_ORIGINS=https://api.zhongxiaojie.cn,https://i.zhongxiaojie.cn
CSRF_TRUSTED_ORIGINS=http://127.0.0.1,http://localhost

# Django 缓存(评论 UA/IP 查询结果);推荐 Redis,例如 redis://127.0.0.1:6379/1
# 留空则使用 LocMem(仅开发、单进程)
# DJANGO_CACHE_REDIS_URL=redis://127.0.0.1:6379/1
#
# WordPress Object Cache Pro(可选):Django 直写评论后用于定向清理评论缓存。
# 请与 WordPress 端 WP_REDIS_CONFIG 的 host/db/prefix 保持一致。
# 例如 WP_REDIS_CONFIG 里 database=5,则这里应为 redis://127.0.0.1:6379/5
# WP_OBJECT_CACHE_REDIS_URL=redis://127.0.0.1:6379/<database>
# 注意:当前定向清理实现依赖 prefix,建议在 WP_REDIS_CONFIG 中显式配置 'prefix' => 'zhxj'
# WP_OBJECT_CACHE_REDIS_PREFIX=zhxj
# WP_OBJECT_CACHE_BLOG_ID=0

# Baby IP Lookup:本机 lookup-ua 与静态资源公网域名(PNG/SVG 补全)
# UA_LOOKUP_UPSTREAM_BASE_URL=http://127.0.0.1:18765
# UA_LOOKUP_PUBLIC_ASSETS_BASE_URL=https://ip.zhongxiaojie.cn
# UA_LOOKUP_DEFAULT_METHOD=ip2location
# UA_LOOKUP_CACHE_TTL=604800

# WordPress database connection (MySQL/MariaDB)
WP_DB_NAME=wordpress
WP_DB_USER=root
WP_DB_PASSWORD=
WP_DB_HOST=127.0.0.1
WP_DB_PORT=3306

# WordPress table prefix, e.g. wp_ / wp123_
WP_TABLE_PREFIX=wp_

# 是否信任反代/CDN 转发头(CF-Connecting-IP / X-Real-IP / X-Forwarded-For),默认开启。
# - 生产推荐开启,并配置 TRUSTED_PROXY_IP_RANGES,只信任你的网关/CDN 回源 IP 段
# - 若 API 不会被公网直连,且 CDN 回源 IP 经常变:可保持开启并留空 TRUSTED_PROXY_IP_RANGES(有伪造风险)
TRUST_PROXY_HEADERS=1
# 反代终止 TLS(如 Nginx/Edge/CDN)时建议开启,配合 X-Forwarded-Proto 识别 https
SECURE_PROXY_SSL_HEADER_ENABLED=1

# 额外输出“真实 IP access log”(Daphne 的 access log 里显示的是 CDN 节点 IP)
# 打开后会在 stdout 输出形如:[realip] ip=... remote=... status=... GET /api/...
REAL_IP_ACCESS_LOG_ENABLED=0

# 受信任反向代理 / CDN 的 IP 段(CIDR,逗号分隔)。
# 仅当请求来源 REMOTE_ADDR 命中这些 IP 段时,后端才会信任 CF-Connecting-IP / X-Real-IP / X-Forwarded-For。
# - 本机 Nginx 反代:127.0.0.1/32,::1/128
# - 生产:把你的 Nginx/网关内网地址段、或 CDN 回源 IP 段加入这里
TRUSTED_PROXY_IP_RANGES=127.0.0.1/32,::1/128

# API 请求签名(HMAC + ts + nonce)——默认关闭
# 注意:这是“请求验签”,不是“返回加密”。建议仅在 HTTPS 下启用。
# API_SIGNING_ENABLED=1
# API_SIGNING_SECRET=change-me-long-random
# 允许客户端时间漂移(秒),超出即拒绝(防离线重放)
# API_SIGNING_TTL_SECONDS=60
# nonce 去重缓存 TTL(秒),建议 >= API_SIGNING_TTL_SECONDS
# API_SIGNING_NONCE_TTL_SECONDS=300
# 需要签名的路径前缀(逗号分隔)
# API_SIGNING_REQUIRED_PREFIXES=/api/
# 免签路径(逗号分隔,严格 path 匹配),例如健康检查:
# API_SIGNING_EXEMPT_PATHS=/api/health/,/api/ping/

# SMTP / Email backend (Django)
# 不配置则不会真的发出邮件(除非你使用本地控制台邮件后端等)。
# EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
# EMAIL_HOST=smtp.example.com
# EMAIL_PORT=587
# EMAIL_USE_TLS=1
# EMAIL_HOST_USER=your-account@example.com
# EMAIL_HOST_PASSWORD=your-app-password
# DEFAULT_FROM_EMAIL="obaby <no-reply@zhongxiaojie.cn>"
#
# 评论回复邮件通知(前台回复他人评论时)
# COMMENT_REPLY_NOTIFICATION_ENABLED=1
# COMMENT_REPLY_EMAIL_FROM="obaby <no-reply@zhongxiaojie.cn>"
# COMMENT_REPLY_EMAIL_HEADER_IMAGE_URL=https://zhongxiaojie.com/wp-content/uploads/2026/01/uugai.com_1661691241113463.png
# COMMENT_REPLY_EMAIL_HEADER_IMAGE_WIDTH=520
# COMMENT_REPLY_EMAIL_HEADER_IMAGE_HEIGHT=180
# COMMENT_REPLY_EMAIL_HEADER_ALT=obaby 𝐢‍𝐧⃝ void
# COMMENT_REPLY_EMAIL_FOOTER_LINE1=obaby 𝐢‍𝐧⃝ void
# COMMENT_REPLY_EMAIL_FOOTER_LINK_TEXT=oba.by
#
# 与 WordPress CREN 插件退订链接校验一致(取自 wp-config.php)
# WORDPRESS_AUTH_KEY=
# WORDPRESS_AUTH_SALT=
# 与 WordPress 登录 Cookie(wordpress_logged_in_*)校验一致(同样取自 wp-config.php)
# 推荐配置 LOGGED_IN_KEY / LOGGED_IN_SALT;留空时后端会回退到 AUTH_KEY / AUTH_SALT
# WORDPRESS_LOGGED_IN_KEY=
# WORDPRESS_LOGGED_IN_SALT=

# 服务器状态小组件:统计磁盘路径(Linux "/";Windows "C:\\")
# SERVER_PROBE_DISK_PATH=/

# 
列表头像:Gravatar 兼容镜像根(路径同 /avatar/{md5}?s=&d=),默认 gg.lang.bi # GRAVATAR_AVATAR_BASE_URL=https://gg.lang.bi # 侧边栏「近期文章」:正文无图时的缩略图回退地址 # SIDEBAR_RECENT_POST_FALLBACK_IMAGE_URL=https://zhongxiaojie.cn/wp-content/uploads/2026/01/... # 评论反垃圾分类(可选;不配置则不调服务、新评论直接通过) # BABY_ANTI_SPAM_CLASSIFY_URL=http://192.168.1.8:8765/v1/classify # BABY_ANTI_SPAM_SECRET=change-me-long-random # BABY_ANTI_SPAM_TIMEOUT=3 # 同一邮箱+IP 对同一篇文章连续提交的最短间隔(秒,0 关闭,最大 120);依赖 Django cache # COMMENT_SUBMIT_COOLDOWN_SECONDS=0 # 前台文章评论列表分页(GET /api/wp/posts/:id/comments/):按一级评论(线程)分页,每页含该层全部回复;不传 page 时默认最后一页(最新线程) # WP_COMMENTS_PER_PAGE=50 # 客户端 ?per_page= 的上限(不超过 500) # WP_COMMENTS_MAX_PER_PAGE=200 # 顶层线程展示:desc=递减(最新在上,默认);asc=递增(最新在下) # WP_COMMENTS_ORDER=desc # Nginx FastCGI 缓存:评论审核通过(comment_approved=1)后清理文章页、首页(可选分类页) # 与 WordPress 插件「Nginx FastCGI Cache Purge on Comment」类似:HTTP GET {站点}/purge{路径} # NGINX_CACHE_PURGE_ENABLED=1 # NGINX_PURGE_PUBLIC_BASE_URL=https://你的域名 # NGINX_PURGE_TIMEOUT=2 # NGINX_PURGE_SSL_VERIFY=1 # NGINX_PURGE_CATEGORIES=1 # NGINX_CACHE_FILES_PATH=/var/cache/nginx/allinone # Kama WP Smile:评论表情包资源(给前端下发,避免硬编码域名) # 若留空,前端会回退使用自身默认/环境变量配置。 # SMILE_PACK_BASE_URL=https://zhongxiaojie.cn/wp-content/plugins/kama-wp-smile-packs/qip_dark_all/ # SMILE_PACK_EXT=gif # SMILE_PACK_TOKENS=smile,sad,laugh,rofl,blum,kiss,yes,no,good,bad,unknw,sorry,pardon,wacko,acute,boast,boredom,dash,search,crazy,yess,cool,air_kiss,angel,bb,beach,aggressive,blush,bomb,bravo,buba,bye,cry,curtsey,dance,dash2,declare,diablo,don-t_mention,drinks,focus,fool,friends,gamer,give_rose,heart,help,hi,laugh1,mail,mda,mosking,music,negative,ok,popcorm,punish,rtfm,sarcastic,secret,shock,shout,thank_you,vava,victory,beee,big_boss,wink,yu,cray2,dash3,girl_pinkglassesf,girl_prepare_fish,locomotive,lazy2,agree,feminist,fuk,fuck,jester,hunter,moil,offtopic,paladin,shablon_01,spam,vinsent,warning,yahoo,superman,girl_witch,fans,beta,butcher,elf,first_move,gamer2,girl_cray2,girl_cray,girl_blum,girl_dance,girl_crazy,girl_haha,heat,hysteric,nhl_crach,nhl_fight,pig_ball,aikido,angry2,banned,alcoholic,bb2,flood,gamer3,girl_devil,flirt,girl_cray3,girl_drink,girl_hide,girl_hospital,girl_impossible,girl_in_love,girl_mad,girl_sad,girl_sigh,girl_smile,girl_to_take_umbrage,girl_wacko,lazy1,nono,man_in_love,party,scenic,queen,paint,crazy_pilot,dwarf,hang1,haha,grin,good3

好处呢,就是所有的系统配置基本都在这个配置文件中控制即可,无需去各种地方设置了,修改之后重启服务即可。

之所以说是玩具,其实我在wp之外添加了另外一个简单的管理后台,这也是为什么选了django 而没有直接用fastapi。

这个东西最初的目的也不是为了替换wp,所以很多功能也没必要再实现一遍了。基础的操作还是在wp的后台完成。

当然,做完折腾到零点多,补全了一些功能之后,最终还是上线了,这就是目前看到的页面效果,lighthouse测试:

ipv4测试:

ipv6测试:

对于wp的主题,也修改了下页面宽度,与现在的vue的页面宽度基本一致了:

http://zhongxiaojie.com

代码地址:

https://gitee.com/obaby/baby-press-public

  •  

Python中的类和对象

未完待续

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__(),否则最后一个覆盖前面的。

构造方法就是一种实例方法

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)
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__

⚠️ 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.__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

还可以实现工厂方法,例如:

class Person:    @classmethod    def create(cls, name):        return cls(**{'name': name})    def __init__(self, name):        self.name = nameif __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        passif __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 selfif __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 dataclassfrom datetime import date, datetime@dataclassclass 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 = nameif __name__ == '__main__':    p = Person('liming')    print(p.name)
  •  

开源项目目录📇

部分开源项目源码。

PHP8 探针项目(包含WP插件)

专业的服务器监控和管理工具,提供实时系统监控、性能测试、数据库检测等功能。本项目包含两个版本:独立PHP探针和WordPress插件版本。

 项目结构

php8-probe/
├── phpprobe.php              # 独立PHP探针(可直接访问)
├── php-probe-widget/         # WordPress插件版本
│   ├── php-probe-widget.php  # 主插件文件
│   ├── includes/             # 小组件类
│   ├── css/                  # 前端样式
│   ├── js/                   # 前端脚本
│   └── README.md             # 插件详细文档
├── LICENSE                   # 许可证
└── README.md                 # 本文件

🚀 快速开始

方式一:独立PHP探针

  1. 将 phpprobe.php 上传到您的Web服务器
  2. 通过浏览器访问该文件即可查看服务器信息
  3. 支持实时监控、性能测试、数据库检测等功能

特点:

  • ✅ 无需安装,直接使用
  • ✅ 单文件部署,简单方便
  • ✅ 支持多平台(Linux、Windows、macOS、FreeBSD)
  • ✅ 实时系统监控

方式二:WordPress插件

  1. 将 php-probe-widget 文件夹复制到 wp-content/plugins/ 目录
  2. 在WordPress后台激活”服务器监控探针”插件
  3. 进入 外观 > 小组件 页面
  4. 将”服务器监控探针”小组件拖拽到侧边栏
  5. 配置显示选项和主题设置

https://gitee.com/obaby/php8-probe


结巴分词HTTP服务

基于Flask和jieba的本地HTTP分词服务。

https://gitee.com/obaby/baby-jb-server


WordPress 博客数据分析工具

这是一个用于分析 WordPress 博客数据的 Python 工具,可以通过 WordPress REST API 获取并分析博客的文章和评论数据。

功能特性

  • 📝 统计指定年份发布的文章数量(按月统计)
  • 💬 统计指定年份的评论数量
  • 🏆 分析评论用户的评论数排行
  • 💾 将分析结果保存为 JSON 文件

https://gitee.com/obaby/baby-wp-data-analysis-tool


微信双开脚本 (WeChat Dual Launch Script)

一个用于 macOS 系统的微信双开自动化脚本,通过复制微信应用并修改 Bundle ID 实现真正的微信双开功能。

📋 功能特性

  • ✅ 一键双开 – 自动完成所有设置步骤
  • ✅ 智能检测 – 自动检测已存在的 WeChat2.app
  • ✅ 安全可靠 – 完善的错误处理和权限检查
  • ✅ 彩色输出 – 友好的命令行界面
  • ✅ 进程管理 – 查看和管理微信进程
  • ✅ 自动化设置 – 无需手动执行复杂命令

https://github.com/obaby/baby-wechat


Baby 足迹地图

 

简介:

 

基于百度地图的足迹地图。
启动服务之后,先去后台 地图 key 设置页面,添加百度地图浏览器端 ak!
启动服务之后,先去后台 地图 key 设置页面,添加百度地图浏览器端 ak!
启动服务之后,先去后台 地图 key 设置页面,添加百度地图浏览器端 ak!

为了防止 js 地址解析受限,需要同时添加服务端 ak!
为了防止 js 地址解析受限,需要同时添加服务端 ak!
为了防止 js 地址解析受限,需要同时添加服务端 ak!

添加之后,访问: http://127.0.0.1:10099/api/location/process-my-location/ 地址刷新数据库的地点坐标信息,后续无需再通过 js 接口进行解析!

https://github.com/obaby/BabyFootprintV2


Simple microblogging

Add a microblog to your site; display the microposts in a widget or using a shortcode. 
增强版优化页面显示,增加分页功能。wp微博插件。

 

https://github.com/obaby/Simple-microblogging-wordpress-plugin


Baby WP 评论强化拦截插件

 

一个强大的WordPress评论过滤插件,支持字数限制、中文检测、关键词过滤等功能。

插件信息

 

  • 插件名称: Baby WP 评论强化拦截插件
  • 版本: 1.0.5
  • 作者: obaby
  • 作者网址https://h4ck.org.cn
  • 许可证: GPL v2 or later

功能特性

 

🛡 评论过滤功能

 

  • 字数限制: 设置评论的最少和最多字数
  • 中文检测: 要求评论必须包含中文字符
  • 关键词过滤: 支持自定义关键词和WordPress设置的关键词
  • 正则表达式支持: 支持使用正则表达式进行高级匹配

⚙ 管理功能

 

  • 简单设置界面: 直观的管理后台设置页面
  • 错误消息自定义: 可以自定义各种错误提示消息和标题
  • 统计信息: 记录评论过滤统计信息,支持重置功能
  • WordPress集成: 与WordPress讨论设置完美集成,支持实时预览
  • 设置验证: 完整的输入验证和数据清理机制

🔧 技术特性

 

  • 简单架构: 采用简单的面向对象架构,易于维护
  • 性能优化: 高效的过滤算法,不影响网站性能
  • 兼容性: 支持WordPress 5.0+版本,PHP 7.4+
  • 多语言: 支持多语言环境
  • 数据安全: 完整的输入验证和清理机制
  • 错误处理: 完善的错误处理和日志记录

https://github.com/obaby/baby-wp-comment-filter


WinRAR-Keygen

 

1. What is WinRAR?

 

  • WinRAR is a trialware file archiver utility for Windows, developed by Eugene Roshal of win.rar GmbH.

  • It can create and view archives in RAR or ZIP file formats and unpack numerous archive file formats.

  • WinRAR is not a free software. If you want to use it, you should pay to RARLAB and then you will get a license file named "rarreg.key".

  • This repository will tell you how WinRAR license file "rarreg.key" is generated.

2. How is “rarreg.key” generated?

 

  • WinRAR uses a signature algorithm, which is a variant of Chinese SM2 digital signature algorithm, to process the user’s name and the license type he/she got. Save the result to “rarreg.key” and add some header info, then a license file is generated.

https://github.com/obaby/winrar-keygen


Baby Device Manager

 

一个功能强大的WordPress设备管理系统插件,支持设备分组管理、设备信息管理、自定义排序、状态跟踪等功能。

功能特点

 

  • 设备分组管理
    • 创建和管理设备分组
    • 自定义分组排序
    • 分组描述信息
  • 设备管理
    • 添加/编辑/删除设备
    • 设备状态管理(在售、停售、已售出、维修中、已报废)
    • 设备图片和产品链接
    • 自定义设备排序
    • 设备描述信息
  • 前端展示
    • 响应式布局
    • 按分组分类显示
    • 支持多种排序方式
    • 美观的界面设计
    • 支持自定义每行显示设备数量(1-6个)
  • 其他功能
    • 图片管理:支持设备图片上传和显示
    • 产品链接:支持添加产品详情页链接
    • 状态跟踪:支持多种设备状态管理
    • 自定义排序:支持设备分组和设备的自定义排序

https://github.com/obaby/Baby-Device-Manager


RSS Beauty

 

为 WordPress RSS Feed 提供美观的网页展示样式(基于 RSS.Beauty 的 Pink 主题)。

项目功能

 

  • RSS 样式化:在 Feed 中注入 XSL 样式表,浏览器打开 feed 地址时以 HTML 页面形式展示,而非原始 XML。
  • Feed Content-Type:将 feed 的 Content-Type 设为 application/xml,使浏览器按 XML 解析并应用 xml-stylesheet
  • XSL 地址:样式表使用插件目录下的静态文件 pink.xsl。需在 OpenResty/Nginx 中为 .xsl 配置正确的 Content-Type(见下方配置说明),否则浏览器可能不按 XSL 解析。
  • 主题:内置淡粉色(light pink)页面背景与适配的文字颜色。

https://cnb.cool/oba.by/rss-beauty


WP-UserAgent

 

Contributors: obaby
Donate Link: https://oba.by
Tags: useragent, user-agent, user agent, web, browser, web browser, operating system, platform, os, mac, apple, windows, win, linux, phone
Requires at least: 2.0
Tested up to: 6.3
Stable tag: 16.06.99

IP 查询方式(归属地)

 

插件支持四种 IP 查询方式,可在 设置 → WP-UserAgent 中选择:

方式 说明
IP2Location 使用 IP2Location 数据库(需将 BIN 文件放入 show-useragent/ip2location_db/db/),依赖 Composer
CZDB 使用纯真 CZDB 数据库(需授权与 db 文件放入 show-useragent/czdb/db/),依赖 Composer
ip2region 使用 ip2region xdb(仅内置 ip2reginapi,不依赖 Composer)。需将 xdb 文件放入 show-useragent/ip2region_db/,文件名:ip2region_v4.xdbip2region_v6.xdb
纯真QQWRY 使用 qqwry_api(qqwry.dat + ipv6wry.db),无需 Composer。数据文件放入 show-useragent/qqwry_api/ipdata/

选择 ip2region 或 纯真QQWRY 时不会加载 vendor/autoload.php。若选择 IP2Location 或 CZDB 时 vendor 加载失败,插件会自动回退为 ip2region 模式,避免站点白屏。

Description

 

WP-UserAgent is a simple plugin that allows you to display details about a computer’s operating system or web browser that your visitors comment from.

It uses the comment->agent property to access the User-Agent string. Through a series of regular expressions, this plugin is able to detect the operating system and browser which can be integrated in comments or placed in custom places through your template(s).

I’m adding new web browsers and operating systems frequently, as well as updating and optimizing the source code. Your feedback is very important, new features have been added by request, so if there’s something you would like to see in WP-UserAgentleave a comment, and I’ll see what I can do.

WP-UserAgent was written with Geany – http://www.geany.org/
Images created with The Gimp – http://www.gimp.org/

注意:

  • 使用 CZDB 时:若更新替换纯真数据库,请同步更新 show-useragent/ip2c-text.php 中的 $key = 'n2pf2+PrE1y9I55MjdpLpg==';
  • 使用 ip2region 时:将 xdb 文件放入 show-useragent/ip2region_db/ip2region_v4.xdbip2region_v6.xdb),无需 Composer。

https://cnb.cool/oba.by/wp-useragent

 

 

  •  

Python的函数

1.定义

1.函数代码以def关键字开头,后接函数标识符名称和圆括号()
2.任何传入的参数和自变量必须放在圆括号内,圆括号中定义参数
3.函数内容以冒号:开始,需要缩进
4.return表达式结束函数,返回一个值给调用方,不带表达式的return相当于返回None
5.若暂无函数实现,函数体内写一个pass关键字,可避免语法错误

def 函数名(参数列表):    函数体    return 返回值

例:

def greet(name, age):    print("Hello World")

2.类型注解 Type Hints

Python函数参数不需要声明类型,解释器会自动推断,但是会带来难以理解,隐藏BUG,开发效率低下等问题,因此类型注解通过引入可选的类型信息解决这些问题,明确指出函数和返回值的类型,让代码更加健壮可维护。

在实现复杂逻辑和对外提供公共接口时,都应当使用类型注解

例:
func.py

def greet(name: str, age: int) -> str:    return f"Hello  World {name}, age {age}"

但是,调用时仍然可以不遵守

func.py

def greet(name: str, age: int) -> str:    return f"Hello  World {name}, age {age}"if __name__ == '__main__':    print( greet('lzj', 12.6) )

此时可以通过mypy工具包,对我们的代码进行检测,会检测出问题

先安装mypy工具 pip install mypy,再进行检测

PS D:\PycharmProjects\python-lang-test> mypy .\func\func.pyfunc\func.py:5: error: Argument 2 to "greet" has incompatible type "float"; expected "int"  [arg-type]Found 1 error in 1 file (checked 1 source file)

如果要彻底避免,需要进行严格类型检查

def add(a: int, b: int) -> int:    if not isinstance(a, int):        raise TypeError(f"参数a期望类型int,实际传入{type(a).__name__}")    if not isinstance(b, int):        raise TypeError(f"参数b期望类型int,实际传入{type(b).__name__}")    return a + bresult2 = add('5', 3)print(result2)
Traceback (most recent call last):  File "D:\PycharmProjects\python-lang-test\func\func.py", line 9, in <module>    result2 = add('5', 3)  File "D:\PycharmProjects\python-lang-test\func\func.py", line 4, in add    raise TypeError(f"参数a期望类型int,实际传入{type(a).__name__}")TypeError: 参数a期望类型int,实际传入str

在函数返回值是某种特定类型或None时使用Optional,等价于Union[具体类型, None]

例:dic.get(name)可能返回字典的值,也可能在没有值时返回None

from typing import Optionaldef age(name: str) -> Optional[int]:    dic = {'a':16, 'b':17, 'c':18}    return dic.get(name)if __name__ == '__main__':    print(age('dd'))    print(age('a'))
None16

3.参数

3.1 位置参数

以正确的形式传入函数,数量要和声明时保持一致

from typing import Optionaldef fun(age:int) -> Optional[int]:    print(age)    return agefun(10)

3.2 关键字参数

函数调用使用关键字参数来确定传入的参数值

def fun(age:int, name:str) :    print(age)    print(name)fun(name='lzj', age=10)
10lzj

3.3 默认参数

调用函数时,如果没有传递参数,则会使用默认参数

def fun(name:str = "a", age:int = 25) :    print(f"fun---name:{name} age:{age}")fun()fun("zhangliang")fun("wangqiang", 28)
fun---name:a age:25fun---name:zhangliang age:25fun---name:wangqiang age:28

3.4 “*”可变位置参数

加了*的参数会以元组的形式传入,一般这种参数放在参数列表后面

格式:

def fun([普通参数], *var_args_tuple) :    函数体

例:

def print_info(num, *var_tuple):    print(num)    print(var_tuple)print_info(70,1,2,3,4,5)print_info(80)print_info(90,*(1,2,3,4,5))
70(1, 2, 3, 4, 5)80()90(1, 2, 3, 4, 5)

如果后面还有参数,必须通过关键字参数传入,如果没有给不定长的参数传参,那么得到的是空元组

def print_info1(seq, *var_tuple, age) :    print(seq)    print(var_tuple)    print(age)print_info1(1,20,30,40,50, age = 25)print_info1(70, age = 29)
1(20, 30, 40, 50)2570()29

3.5 “**”可变关键字参数

加了两个星号的参数,会以字典的形式传入,“**”可变参数的后面不能再有其他参数,因此“**”可变参数往往在“*”可变参数的后面(如果有的话),并且作为整个参数列表的最后一个参数

调用时,直接在参数列表直接传入键值对

def func(nid, **info):    print(nid)    print(info)    if __name__ == '__main__':    func(10, name='苗苗', age=22)    func(11, name='欣欣', age=27)
10{'name': '苗苗', 'age': 22}11{'name': '欣欣', 'age': 27}

如果要传字典,需要用**前缀,否则报错

if __name__ == '__main__':    dic = {'name':'苗苗', 'age': 18}    #func(10, dic) ❌    func(10, **dic) #✅    func(10, **{'name':'苗苗', 'age': 18}) #✅

3.6 参数解包/打包

上面出现的*() **{}就是一种解包/打包的写法

概念

  • 打包(Packing):将多个参数合并成一个数据结构(元组或字典)
  • 解包(Unpacking):将一个数据结构拆解成多个参数

异同:

语法含义类型使用场景
*args可变位置参数元组不确定数量的位置参数
**kwargs可变关键字参数字典不确定数量的关键字参数
*args, **kwargs组合使用-通用函数包装、装饰器、继承

总结

  • *args:接收多余的位置参数(arguments)
  • **kwargs:接收多余的关键字参数(keyword arguments)

例:

def func(a, *args, **kwargs):    print(a)    print(args)    print(kwargs)func(100,1,2,3,k1=20, k2=30)func(200,*(1,2,3),**{"k1":20, "k2":30})
100(1, 2, 3){'k1': 20, 'k2': 30}200(1, 2, 3){'k1': 20, 'k2': 30}

例:参数解包

传递参数时,可以将多个实参用*组合为元组,传入形参

def sum_num(a, b, c):    return a+b+cif __name__ == '__main__':    print( sum_num(1, 2, 3) ) #6    print( sum_num( *(1, 2, 3) ) ) #6    let_tuple = (1, 2, 3)    print( sum_num( *let_tuple ) ) #6

4.Lambda匿名函数

python使用lambda来创建匿名函数

  • lambda是一种小型的内联函数,通常只有一行代码
  • 匿名的,只能通过赋值给变量或作为参数传递给其他函数的方式来使用
  • 可以有任意数量的参数,但是只能有一个表达式
  • 不需要使用def关键字定义
  • 通常用于编写简单的单行的函数
  • 通常在函数需要作为参数传递的情况下使用

与java的lambda的区别:通常只有一行代码


组成:

lambda arguments : expression
  • lambda 是python的关键字
  • arguments 参数列表
  • expression 表达式,用于计算并返回结果

例:

某种操作函数operator,传入待操作数a, b以及操作规则func,将1和2以及一个加法计算规则add()传入,令operator函数对两个数进行加法

def add(a, b):    return a + bdef operator(a, b, func):    return func(a, b)print( operator(1, 2, add) ) # 3

add()就无需单独再定义函数,可以匿名成为一个加法计算规则的lambda:lambda x, y: x + y

print( operator(1, 2, lambda x, y: x + y ) ) #3

lambda可以赋给变量,称为函数变量,函数变量保存的是函数类型的值,对函数变量进行调用(加小括号),才能得到对应函数的返回值类型的值

例:

f = lambda: "Hello, world!"print(f) # <function <lambda> at 0x000002A2226B7C40>print(f())  # Hello, world!
x = lambda a, b, c : a + b + cprint(x(5, 6, 2)) #13

在python中,lambda通常可以和一些内置函数配合使用,实现一些效果

numbers = [1, 2, 3, 4, 5, 6, 7, 8]even_numbers = list(filter(lambda x: x % 2 == 0, numbers))print(even_numbers)  # 有三名学生的姓名和年龄,按年龄排序student_list = [{"name": "z3", "age": 36}, {"name": "li4", "age": 14}, {"name": "w5", "age": 27}]print(sorted(student_list, key=lambda x: x["age"]))# map() 的主要作用是将给定的函数批量应用到可迭代对象的每个元素上,实现数据转换map_result = map(lambda x: x * x, [0, 1, 3, 7, 9])print(list(map_result)) # filter() 的主要作用是将给定的函数**批量应用**到可迭代对象的每个元素上,实现数据过滤filter_result = filter(lambda x: x >= 0, [-0, -1, -3, 7, 9])print(list(filter_result))  
[2, 4, 6, 8][{'name': 'li4', 'age': 14}, {'name': 'w5', 'age': 27}, {'name': 'z3', 'age': 36}][0, 1, 9, 49, 81][0, 7, 9]

5.闭包 (Closure)

闭包是python实现函数式编程的重要基础

定义:内部函数引用了嵌套它的外部函数的变量或参数,且内部函数被返回或暴露出去,从而保留了外部函数作用域(即便已经调用完成)的函数对象。

例:定义一个函数lazy_sum(),嵌套一个内部函数inner(),并将内部函数inner()作为返回值返回,并赋值给了num,但是是个函数对象,只有执行num(),内部函数inner()才会执行,将外部函数的参数*args进行累加。在这个过程中lazy_sum()的执行完成后,并没有被释放,参数*args也被保留下来,什么时候内部函数执行了,什么时候真正完成调用。

def lazy_sum(*args):    def inner():        result = 0        for n in args:            result = result + n        return result    #返回函数    return innernum = lazy_sum(11, 12)print(num) # <function lazy_sum.<locals>.inner at 0x0000023A15FD62A0>print(num()) # 23print(num.__closure__) #闭包保存的变量元组 (<cell at 0x00000241568DB5E0: tuple object at 0x00000241568E8940>,)print(num.__closure__[0].cell_contents) # 闭包保存的变量 (11, 12)

例:带参数

def hello(s1):    def world(s2):        return s1+' '+s2    #返回函数    return worldfun = hello('hello')print(fun('world')) #hello worldprint( hello('hello')('world') ) #hello world

总结:

  • 函数要嵌套,支持多层
  • 内部函数要引用外部函数的局部变量
  • 外部函数将内部函数返回,或作为参数传递

利弊:

  • 优点: 封装隐藏数据和持久化状态,简化参数传递,适合实现轻量级状态保持场景
  • 缺点: 内存占用过高,可读性和可调试性差,存在循环变量捕获陷阱,使用时需要避免持有占用大空间的数据

6.装饰器(Decorators)

装饰器类似Spring AOP,前置/后置通知

装饰器(decorators)是Python中的一种高级功能,它允许你动态地修改函数或类的行为。

装饰器本质上是一种函数,它接受一个函数作为参数并返回一个新的函数或修改原来的函数,可以实现不修改被修饰对象的源码和调用方式的前提下,为其额外添加功能(日志,计时,权限校验等)。

执行原理:

  1. 绑定原函数
  2. 定义额外功能
  3. 替换,返回包装函数
  4. 调用,执行原逻辑+增强逻辑

例:装饰器的实现,使用aop()函数增强hello()函数,aop()函数替换掉原hello()函数,直接调用aop()函数

def aop(func):    def wrapper():        print("before")        func()        print("after")    return wrapperdef hello() :    print('hello world')if __name__ == '__main__':    aop(hello)()
beforehello worldafter

例:简化写法,@加上一个闭包函数,就能构成一个装饰器函数,语法类似Java中的注解

def aop(func):    def wrapper():        print("before")        func()        print("after")    return wrapper@aopdef hello() :    print('hello world')if __name__ == '__main__':    hello()
beforehello worldafter

例:带参数的装饰器

def aop(func):    def wrapper(*args, **kwargs):        print("before")        func(*args, **kwargs)        print("after")    return wrapper@aopdef hello(name) :    print(f'hello,{name}')if __name__ == '__main__':    hello('sxh')
  •