注册
环信即时通讯云

环信即时通讯云

单聊、群聊、聊天室...
环信开发文档

环信开发文档

环信FAQ

环信FAQ

集成常见问题及答案
RTE开发者社区

RTE开发者社区

汇聚音视频领域技术干货,分享行业资讯
技术讨论区

技术讨论区

技术交流、答疑
资源下载

资源下载

收集了海量宝藏开发资源
iOS Library

iOS Library

不需要辛辛苦苦的去找轮子, 这里都有
Android Library

Android Library

不需要辛辛苦苦的去找轮子, 这里都有

成为海王的日子——我做了一个微信自动聊天的工具

一直幻想着能够成为一个海王,于是做了一个微信自动聊天的工具。 测试微信版本:wechat 3.9.12.17 采用技术: Bmob后端云(AI对话和存储需要自动聊天的人和prompt) uiautomation pyautogui 开发语言: pytho...
继续阅读 »

一直幻想着能够成为一个海王,于是做了一个微信自动聊天的工具。


测试微信版本:wechat 3.9.12.17


采用技术:



  • Bmob后端云(AI对话和存储需要自动聊天的人和prompt)

  • uiautomation

  • pyautogui


开发语言:



  • python,conda环境下运行


最终的效果大家可以看B站:http://www.bilibili.com/video/BV1yK…


一、获取微信对话内容


这里采用了网上的一些开源项目进行修改,写了一个自己的WeChat控制类,采用的是uiautomation和pyautogui组件进行UI的控制,模拟人的操作。


WeChat控制类的内容比较多,为了方便阅读,这里只呈现一部分,有需要的朋友可以联系我获取。


class WeChat:
def __init__(self, path, locale="zh-CN"):
# 微信打开路径
self.path = path

# 用于复制内容到剪切板
self.app = QApplication([])

self.lc = WeChatLocale(locale)

# 鼠标移动到控件上
def move(self,element):
x, y = element.GetPosition()
auto.SetCursorPos(x, y)

# 鼠标快速点击控件
def click(self,element):
x, y = element.GetPosition()
auto.Click(x, y)

# 鼠标右键点击控件
def right_click(self,element):
x, y = element.GetPosition()
auto.RightClick(x, y)


# 鼠标快速点击两下控件
def double_click(self,element):
x, y = element.GetPosition()
auto.SetCursorPos(x, y)
element.DoubleClick()


# 打开微信客户端
def open_wechat(self):
subprocess.Popen(self.path)

# 搜寻微信客户端控件
def get_wechat(self):
return auto.WindowControl(Depth=1, Name=self.lc.weixin)

# 防止微信长时间挂机导致掉线
def prevent_offline(self):
self.open_wechat()
self.get_wechat()

search_box = auto.EditControl(Depth=8, Name=self.lc.search)
self.click(search_box)

# 搜索指定用户
def get_contact(self, name):
self.open_wechat()
self.get_wechat()

search_box = auto.EditControl(Depth=8, Name=self.lc.search)
self.click(search_box)

pyperclip.copy(name)
auto.SendKeys("{Ctrl}v")

# 等待客户端搜索联系人
time.sleep(0.3)
search_box.SendKeys("{enter}")

# 鼠标移动到发送按钮处点击发送消息
def press_enter(self):
# 获取发送按钮
send_button = auto.ButtonControl(Depth=15, Name=self.lc.send)
self.click(send_button)

# 检测微信发新消息的用户
def check_new_user(self):
self.open_wechat()
self.get_wechat()

users = []

# 获取左侧聊天按钮
chat_btn = auto.ButtonControl(Name=self.lc.chats)
self.double_click(chat_btn)

# 持续点击聊天按钮,直到获取完全部新消息
item = auto.ListItemControl(Depth=10)
prev_name = item.ButtonControl().Name
while True:
# 判断该联系人是否有新消息
pane_control = item.PaneControl()
if len(pane_control.GetChildren()) == 3:
users.append(item.ButtonControl().Name)

self.click(item)

# 跳转到下一个新消息
self.double_click(chat_btn)
item = auto.ListItemControl(Depth=10)

# 已经完成遍历,退出循环
if prev_name == item.ButtonControl().Name:
break

prev_name = item.ButtonControl().Name

return users


二、获取需要自动聊天的人和对应的prompt


这部分信息我存储在Bmob后端云上面,对应的表结构(表名为:autochat,创建的字段为:nameprompt)和测试的内容如下:


image.png


获取信息的时候,我采用了子线程的方式,每隔300秒获取一次新的需要自动对话的微信和对应的prompt,代码如下:


# Bmob对象
bmob = Bmob(config['bmob_appid'], config['bmob_secret'])

# 存储从Bmob后端云获取到的自动对话的微信名和对应的prompt
name_prompts = {}

# 从Bmob后端云获取自动聊天的微信名和prompt
def get_user_prompt():
users = bmob.findObjects('autochat')
name_prompts.clear()
for user in users:
name_prompts[user.name] = user.prompt

# 每隔5分钟获取一次要自动聊天微信名称和对应的prompt
def run_with_interval():
while True:
get_user_prompt()
time.sleep(300)

if __name__ == "__main__":
t = threading.Thread(target=run_with_interval)
t.start()


三、组装对话上下文和自动对话


这里主要用到了WeChat类的获取新对话人名字获取某个人历史聊天记录的方法,和Bmob后端云的AI功能,具体代码如下:


# 执行自动聊天功能
def main():
wechat = WeChat(path=config['wechat_path'])
# 创建一个锁
lock = threading.Lock()

while True:
try:
comtypes.CoInitialize()
# 确保微信操作的线程安全
with lock:
wechat.click(auto.ButtonControl(Name=wechat.lc.facorites))
users = wechat.check_new_user()
if len(users) <= 0:
time.sleep(5)
print("暂时没有新消息")
continue

for user in users:
if user not in name_prompts.keys():
time.sleep(5)
print(f"{user}不在需要自动问答的名单上")
continue
else:
# 获取聊天记录,30这个数字可以调整,越大的话,AI会越了解你,但消耗的token也越多
msgList = wechat.get_dialogs(user, 30)
if len(msgList) <= 0:
continue

# 组装上下文对话记录
ai = []
ai.append({"content": name_prompts[user], "role": "system"})
for msg in msgList:
chatmsg = msg[2].replace('[动画表情]','')
if chatmsg=='':
continue

if msg[1] == user:
ai.append({"content": chatmsg, "role": "user"})
else:
ai.append({"content": chatmsg, "role": "assistant"})

bmob.connectAI()
to = bmob.chat2(ai)
bmob.closeAI()
print('ai:'+to)

if to != "":
wechat.send_msg(user, to)
except Exception as e:
print(e)

作者:小小琪_Bmob后端云
来源:juejin.cn/post/7422401535215435788
收起阅读 »

上6休3上3休2……,为了理清这烧脑的调休安排我制作一个调休日历!

调休日历 前些天,使用python做了一个日历,可以打印出调休的节假日。效果还可以,但是遇到了一些小问题。 有一个朋友提出了这样一个问题:“日历确实是很好,但是,使用控制台print不怎么样,别人的日历都是很好看的,而我们的日历还只能用print。这太丢人了,...
继续阅读 »

调休日历


前些天,使用python做了一个日历,可以打印出调休的节假日。效果还可以,但是遇到了一些小问题。


有一个朋友提出了这样一个问题:“日历确实是很好,但是,使用控制台print不怎么样,别人的日历都是很好看的,而我们的日历还只能用print。这太丢人了,我出门说自己是你的粉丝,都没什么面子了啊!”


还有一个朋友提出了另外一个问题:“在我们国家,‘农历’也是很重要的,比如说‘农历新年’是一年中最重要的日子。所以,我们的日历也应该能看到农历才比较好。”


好吧,这次就来解决一下这些问题!


农历


农历介绍


农历,也称为“阴历”,“月亮历”,“阴阳历”,是一种重要的历法,起源于中国的古代,这种历法同时结合了太阳运动,和月亮周期。其中月球围绕地球旋转的周期,大约为29.5天,这构成了农历的一个月,而一年通常是12个月。然而,一个太阳年(太阳公转一周)大概是365.24天,显然月亮的12个月不够这个数字,因此,为了填补到太阳年的时长,就会加入“闰月“,大概每3年就会出现一个”闰月“,”闰月“和公历中的“闰年”有异曲同工之妙。


那么农历有什么用呢?农历有很大的作用!节日安排,就是农历决定的(新年,中秋,端午等)。农业活动也和农历有很大的关系,比如说,仍然有很多农民,根据农历的指导,进行农业活动,什么时候播种,什么时候收割,都是农历决定的。还有很多人过生日,都会选择过农历生日。除此之外,像“季节变化”也和农历有微妙的关系,我们经常可以听到,“现在已经立秋了,已经是秋天了,马上就要凉快了。”,诸如此类的话,可见,农历在日常生活中发挥了重要作用。是我们祖先的伟大发明。


农历实现


那么,农历到底是怎么确定日子的呢?这和天文观测有很大的关系。我们要通过观察天象,来确定农历的准确性。比如说,在中国古代,就有专门的机构,如皇家天文台负责观测天文,然后调整历法,这是一个重要的活动。在历史上,历法也经过了多次修订。


到了现代,对于天文的观测,不再是必须的了。因为现代的科技较为发达,已经能够通过数学计算,历史天文数据推导等,精确的计算出农历。因此,即使我们没有“夜观星象”,也可以知道未来上百年的农历运作。


但是,我们既不会观察天象,也不会科学计算月亮运动,怎么办呢?当然没关系啦,因为,别人已经算好了,我们直接引用就可以了!


安装:pip install lunarcalendar


from lunarcalendar import Converter, Solar, Lunar


# 将公历日期转换为农历
solar = Solar(2024, 9, 17)
lunar = Converter.Solar2Lunar(solar)


# 输出结果为农历日期
print(lunar)


# 将农历日期转换为公历
lunar = Lunar(2024, 8, 15)
solar = Converter.Lunar2Solar(lunar)


# 输出结果为公历日期
print(solar)

转换为汉字日期


一般在农历中,我们并不使用阿拉伯数字的记录,而是常说,“正月初三”,“八月廿二”,这样的表达方式。因此,我们还需要将数字的农历,转为常见的汉字日期:


from lunarcalendar import Converter, Solar




def lunar_date_str(year, month, day):
lunar = Converter.Solar2Lunar(Solar(year, month, day))
leap_month = lunar.isleap
months = ["正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "冬月", "腊月"]
days = ["初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十",
"十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
"廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"]
month_str = months[lunar.month - 1]
if leap_month:
month_str = "闰" + month_str
day_str = days[lunar.day - 1]

# 该实现是特别为了符合日历的实现,仅在每个月的第一天返回月份,例如正月初一,返回“正月”
# 而其他日子,不返回月份,仅返回日期,例如正月初三,返回“初三”
if lunar.day == 1:
return month_str
else:
return day_str

如果要更广泛意义的月份加日期,只需要简单的修改返回值即可:f"{month_str}{day_str}"


日历实现


假期判断


因为“调休”的存在,所以我们的放假日期不总是固定的,每年都会有很大的变化,那么如何判断某个日子是不是节假日呢?是不是需要调休呢?


实际上,这是一件困难的事情,没有办法提前知道,只能等到每一年,国家公布了放假安排以后,我们才能够知道准确的放假调休日期。比如说,一些日历等不及,还没等公布放假安排,就已经开始提前印刷了,那么这样的日历,其上包含的信息,就是不完整的,他只能告诉你常规的节日和星期,没办法告诉你调休。


看过我上一期关于日历文章的,应该知道在当时,我是使用了“标记调休”的方式,实现这一点的,大概像这样:


rili2.png


这当然是简单有效,且可行的,只不过一次标记只能管一年,到了明年就不能用了,还得自己重新标记,况且,标记也是一件麻烦的事情,有没有什么更好的办法呢?


当然是有的,我们让别人给我们标记好了,自己等着用现成的,不就好了吗?那么哪里能找到这样的好心人呢?当然是有的,python有一个库叫做chinese-calendar,其中维护了每年的中国节假日,我们只需要使用这个库,让他告诉我们,今天休不休息,就好了。


安装:pip install chinese_calendar


import chinese_calendar as cc
from datetime import date


# 检查某一天是否是工作日或节假日
on_holiday, holiday_name = cc.get_holiday_detail(date(2024, 10, 1))


# 输出是否为假日,假日名称
print(on_holiday, holiday_name)

唉,世界上还是好人多啊!用上了这个,我们可以省很多事,真是好东西啊。


matplotlib绘制日历


matplotlib非常常用,今天就不主要介绍了,虽然它不常用于绘制日历,但是,它的功能其实是很广泛的,包括我们今天的绘制日历。


在下面的实现中,允许提供一个额外信息表,来覆盖默认的农历。也就是你可以通过extra_info_days来增加节日,纪念日的提示。


import datetime
import chinese_calendar as cc
import matplotlib.pyplot as plt


from calendar import monthrange
from lunarcalendar import Converter, Solar




class CalendarDrawer:
plt.rcParams['font.family'] = 'SimHei' # 设置显示字体为黑体
months = ["正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"]
days = ["初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十",
"十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
"廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"]


def __init__(self, year, month, extra_info_days=):
self.year = year
self.month = month
self.extra_info_days = extra_info_days or {}
self.fig, self.ax = plt.subplots()


def ax_init(self):
self.ax.axis([0, 7, 0, 7])
self.ax.axis("on")
self.ax.grid(True)
self.ax.set_xticks([])
self.ax.set_yticks([])


@staticmethod
def lunar_date_str(year, month, day):
lunar = Converter.Solar2Lunar(Solar(year, month, day))
month_str = "闰" + CalendarDrawer.months[lunar.month - 1] if lunar.isleap else CalendarDrawer.months[lunar.month - 1]
return month_str if lunar.day == 1 else CalendarDrawer.days[lunar.day - 1]


def plot_month(self):
self.ax.text(3.5, 7.5, f"{self.year}{self.month}月", color="black", ha="center", va="center")


def plot_weekday_headers(self):
for i, weekday in enumerate(["周一", "周二", "周三", "周四", "周五", "周六", "周日"]):
x = i + 0.5
self.ax.text(x, 6.5, weekday, ha="center", va="center", color="black")


def plot_day(self, day, x, y, color):
ex_day = datetime.date(self.year, self.month, day)
day_info = f"{day}\n{self.extra_info_days.get(ex_day, self.lunar_date_str(self.year, self.month, day))}"
self.ax.text(x, y, day_info, ha="center", va="center", color=color)


def check_color_day(self, day):
date = datetime.date(self.year, self.month, day)
return "red" if cc.get_holiday_detail(date)[0] else "black"


def save(self):
self.ax_init()
self.plot_month()
self.plot_weekday_headers()


weekday, num_days = monthrange(self.year, self.month)
y = 5.5
x = weekday + 0.5


for day in range(1, num_days + 1):
color = self.check_color_day(day)
self.plot_day(day, x, y, color)
weekday = (weekday + 1) % 7
if weekday == 0:
y -= 1
x = weekday + 0.5


plt.savefig(f"日历{self.year}-{self.month}.png")




if __name__ == "__main__":
extra_info_days = {
datetime.date(2024, 1, 1): "元旦",
datetime.date(2024, 2, 10): "春节",
datetime.date(2024, 2, 14): "情人节",
datetime.date(2024, 2, 24): "元宵节",
datetime.date(2024, 3, 8): "妇女节",
datetime.date(2024, 4, 4): "清明节",
datetime.date(2024, 5, 1): "劳动节",
datetime.date(2024, 5, 12): "母亲节",
datetime.date(2024, 5, 20): "520",
datetime.date(2024, 6, 1): "儿童节",
datetime.date(2024, 6, 10): "端午节",
datetime.date(2024, 6, 16): "父亲节",
datetime.date(2024, 8, 10): "七夕节",
datetime.date(2024, 9, 17): "中秋节",
datetime.date(2024, 10, 1): "国庆节",
datetime.date(2024, 11, 1): "万圣节",
datetime.date(2024, 11, 11): "双十一",
datetime.date(2024, 12, 12): "双十二",
datetime.date(2024, 12, 24): "平安夜",
datetime.date(2024, 12, 25): "圣诞节"
}


calendar_drawer = CalendarDrawer(2024, 12, extra_info_days) # 第一个参数为年份,第二个参数为月份,第三个参数为额外信息字典
calendar_drawer.save()

绘制结果,2024-09:
日历2024-9.png


2024-10:
日历2024-10.png


2024-11:
日历2024-11.png


2024-12:
日历2024-12.png


引用与致谢


日历样式,部分参考了便民查询:wannianrili.bmcx.com/


matplotlib绘制,部分参考了shimo164:medium.com/@shimo164/


详细的中国节假日,不再需要个人手动标记了,chinese-calendar:github.com/LKI/chinese…


快速将公历转为农历,支持转换到2100年,LunarCalendar:github.com/wolfhong/Lu…


总结


从我们的日历中,可以清晰的看出,中秋到国庆,我们经历了:



  1. 上6休3

  2. 上3休2

  3. 上5休1

  4. 上2休7

  5. 上5休1


嗯,确实是太烧脑了,没有日历,很难算的清楚啊,最后,那么问题来了,3+7到底等于几呢?


作者:瞎老弟
来源:juejin.cn/post/7414013230954774579
收起阅读 »

Python: 深入了解调试利器 Pdb

Python是一种广泛使用的编程语言,以其简洁和可读性著称。在开发和调试过程中,遇到错误和问题是不可避免的。Python为此提供了一个强大的调试工具——Pdb(Python Debugger)。Pdb是Python标准库中自带的调试器,可以帮助开发者跟踪代码执...
继续阅读 »

Python是一种广泛使用的编程语言,以其简洁和可读性著称。在开发和调试过程中,遇到错误和问题是不可避免的。Python为此提供了一个强大的调试工具——Pdb(Python Debugger)。Pdb是Python标准库中自带的调试器,可以帮助开发者跟踪代码执行、查看变量值、设置断点等功能。本文将详细介绍Pdb的使用方法,并结合实例展示其强大的调试能力。


Python-Debugging-With-Pdb_Watermarked.webp


1. Pdb简介


Pdb是Python内置的调试器,支持命令行操作,可以在Python解释器中直接调用。Pdb提供了一系列命令来控制程序的执行,查看和修改变量值,甚至可以在运行时修改代码逻辑。


2. 如何启动Pdb


在Python代码中启动Pdb有多种方式,以下是几种常见的方法:


2.1 在代码中插入断点

在代码中插入import pdb; pdb.set_trace()可以在运行到该行时启动Pdb:


def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)

import pdb; pdb.set_trace()
print(factorial(5))

2.2 通过命令行启动

可以通过命令行启动Python脚本,并在需要调试的地方使用pdb模块:


python -m pdb myscript.py

3. Pdb的基本命令


Pdb提供了许多命令来控制调试过程,以下是一些常用命令:



  • b (break): 设置断点

  • c (continue): 继续执行程序直到下一个断点

  • s (step): 进入函数内部逐行执行

  • n (next): 执行下一行,不进入函数内部

  • p (print): 打印变量的值

  • q (quit): 退出调试器


4. 实战示例


让我们通过一个具体的例子来演示Pdb的使用。假设我们有一个简单的Python脚本,用于计算列表中元素的平均值:


def average(numbers):
total = sum(numbers)
count = len(numbers)
return total / count

numbers = [1, 2, 3, 4, 5]
print(average(numbers))

4.1 设置断点并启动调试

我们希望在计算平均值之前检查totalcount的值:


import pdb; pdb.set_trace()

def average(numbers):
total = sum(numbers)
count = len(numbers)
return total / count

numbers = [1, 2, 3, 4, 5]
print(average(numbers))

运行上述代码,当程序执行到pdb.set_trace()时,将进入调试模式:


PS C:\src\uml\2024\07> python -m pdb myscript.py
> c:\src\uml\2024\07\myscript.py(1)<module>()
-> import pdb; pdb.set_trace()
(Pdb) n
> c:\src\uml\2024\07\myscript.py(3)<module>()
-> def average(numbers):
(Pdb) m
*** NameError: name 'm' is not defined
(Pdb) n
> c:\src\uml\2024\07\myscript.py(8)<module>()
-> numbers = [1, 2, 3, 4, 5]
(Pdb) n
> c:\src\uml\2024\07\myscript.py(9)<module>()
-> print(average(numbers))
(Pdb) n
3.0
--Return--
> c:\src\uml\2024\07\myscript.py(9)<module>()->
-> print(average(numbers))

4.2 查看变量值

在调试模式下,可以使用p命令查看变量值:


(Pdb) p numbers
[1, 2, 3, 4, 5]
(Pdb)

通过这种方式,可以一步步检查变量的值和程序的执行流程。


5. 高级功能


除了基本命令,Pdb还提供了许多高级功能,如条件断点、调用栈查看等。


5.1 查看调用栈

使用where命令可以查看当前的调用栈:


(Pdb) where
<frozen runpy>(198)_run_module_as_main()
<frozen runpy>(88)_run_code()
c:\users\heish\miniconda3\lib\pdb.py(1952)<module>()->
-> pdb.main()
c:\users\heish\miniconda3\lib\pdb.py(1925)main()
-> pdb._run(target)
c:\users\heish\miniconda3\lib\pdb.py(1719)_run()
-> self.run(target.code)
c:\users\heish\miniconda3\lib\bdb.py(600)run()
-> exec(cmd, globals, locals)
<string>(1)<module>()->
> c:\src\uml\2024\07\myscript.py(9)<module>()->
-> print(average(numbers))

6. 总结


Pdb是Python提供的一个功能强大的调试工具,掌握它可以大大提高代码调试的效率。在开发过程中,遇到问题时不妨多利用Pdb进行调试,找出问题的根源。通过本文的介绍,希望大家能够更好地理解和使用Pdb,为Python编程之路增添一份助力。


作者:王义杰
来源:juejin.cn/post/7392439754678321192
收起阅读 »

我发现了 Android 指纹认证 Api 内存泄漏

我发现了 Android 指纹认证 Api 内存泄漏 目前很多市面上的手机基本都有指纹登陆功能。Google 也提供了调用相关功能 API,安全类的App 也基本都在使用。接下来就一起捋一捋今天的主角 BiometricPrompt 先说问题,使用Biome...
继续阅读 »

我发现了 Android 指纹认证 Api 内存泄漏


目前很多市面上的手机基本都有指纹登陆功能。Google 也提供了调用相关功能 API,安全类的App 也基本都在使用。接下来就一起捋一捋今天的主角 BiometricPrompt


先说问题,使用BiometricPrompt 会造成内存泄漏,目前该问题试了 Android 11 到 13 都发生,而且没有什么好的办法。目前想到的最好的方法是漏的少一点。当然谁有好的办法欢迎留言。


问题再现


先看动画


在这里插入图片描述


动画中操作如下



  1. MainAcitivity 跳转到 SecondActivity

  2. SecondActivity 调用 BiometricPrompt 三次

  3. 从SecondActivity 返回到 MainAcitivity


以下是使用 BiometricPrompt 的代码


public fun showBiometricPromptDialog() {
val keyguardManager = getSystemService(
Context.KEYGUARD_SERVICE
) as KeyguardManager;

if (keyguardManager.isKeyguardSecure) {
var biometricPromptBuild = BiometricPrompt.Builder(this).apply {// this is SecondActivity
setTitle("verify")
setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL or BiometricManager.Authenticators.BIOMETRIC_WEAK)
}
val biometricPromp = biometricPromptBuild.build()
biometricPromp.authenticate(CancellationSignal(), mExecutor, object :
BiometricPrompt.AuthenticationCallback() {

})
}
else {
Log.d("TAG", "showLockScreen: isKeyguardSecure is false");
}
}

以上逻辑 biometricPromp 是局部变量,应该没有问题才对。


内存泄漏如下


在这里插入图片描述
可以看到每启动一次生物认证,创建的 BiometricPrompt 都不会被回收。


规避方案:


修改方案也简单


方案一:



  1. biometricPromp 改为全局变量。

  2. this 改为 applicationContext


方案一存在的问题,SecondActivity 可能频繁创建,所以 biometricPromp 还会存在多个实例。


方案二(目前想到的最优方案):



  1. biometricPromp 改为单例

  2. this 改为 applicationContext


修改后,App memory 中只存在一个 biometricPromp ,且没有 Activity 被泄漏。


想到这里,应该会觉得奇怪,biometricPromp 为什么不会被回收?提供的 API 都看过了,没有发现什么方法可以解决这个问题。直觉告诉我这个可能是系统问题,下来分析下BiometricPrompt 吧。


BiometricPrompt 源码分析


在这里插入图片描述


App 相关信息通过 BiometricPrompt 传递到 System 进程,System 进程再通知 SystemUI 显示认证界面。


App 信息传递到 System 进程,应该会使用 Binder。这个查找 BiometricPrompt 使用哪些 Binder。


private final IBiometricServiceReceiver mBiometricServiceReceiver =
new IBiometricServiceReceiver.Stub() {

......
}

源码中发现 IBiometricServiceReceiver 比较可疑,IBiometricServiceReceiver 是匿名内部类,内部是持有 BiometricPrompt 对象的引用。


接下来看下 System Server 进程信息(注:系统是 UserDebug 的手机,才可以查看,买的手机版本是不支持的)


在这里插入图片描述



😂 App 使用优化后(方案二)App 只存在一个 IBiometricServiceReceiver ,而 system 进程中存在三个 IBiometricServiceReceiver 的 binder proxy。 每次启动 BiometricPrompt 都会创建一个。这个就不解释为什么会出现三个binder proxy,感兴趣可以看下面推荐的文章。GC root 是 AuthSession。

再看下 AuthSession 的实例数


在这里插入图片描述


果然 AuthSession 也存在三个。


在这里插入图片描述


这里有个知识点,binder 也是有生命周期的,三个 Proxy 这篇文章也是解释了的。有兴趣的可以了看下。


Binder | 对象的生命周期


一开始,我以为 AuthSession 没有被置空,看下代码,发现 AOSP 的代码,还是比较严谨的,有置空的操作。


细心的同学发现,上图中 AuthSession 没有被任何对象引用,AuthSession 就是 GC Root,哈哈哈。


问题解密


一个实例什么情况可以作为GC Root,有兴趣的同学,可以自行百度,这里就不卖关子了,直接说问题吧。


Binder.linkToDeath()


public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
}

需要传递 IBinder.DeathRecipient ,这个 DeathRecipient 会被作为 GC root。当调用 unlinkToDeath(@NonNull DeathRecipient recipient, int flags),GC root 才被收回。


AuthSession 初始化的时候,会调用 IBiometricServiceReceiver .linkToDeath。


public final class AuthSession implements IBinder.DeathRecipient {
AuthSession(@NonNull Context context,
......
@NonNull IBiometricServiceReceiver clientReceiver,
......
) {
Slog.d(TAG, "Creating AuthSession with: " + preAuthInfo);
......
try {
mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */);//this 变成 GC root
} catch (RemoteException e) {
Slog.w(TAG, "Unable to link to death");
}

setSensorsToStateUnknown();
}
}

Jni 中 通过 env->NewGlobalRef(object),告诉虚拟机 AuthSession 是 GC Root。


core/jni/android_util_Binder.cpp

static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
jobject recipient, jint flags)
// throws RemoteException
{
if (recipient == NULL) {
jniThrowNullPointerException(env, NULL);
return;
}

BinderProxyNativeData *nd = getBPNativeData(env, obj);
IBinder* target = nd->mObject.get();

LOGDEATH("linkToDeath: binder=%p recipient=%p\n", target, recipient);

if (!target->localBinder()) {
DeathRecipientList* list = nd->mOrgue.get();
sp<JavaDeathRecipient> jdr = new JavaDeathRecipient(env, recipient, list);//java 中 DeathRecipient 会被封装为 JavaDeathRecipient
status_t err = target->linkToDeath(jdr, NULL, flags);
if (err != NO_ERROR) {
// Failure adding the death recipient, so clear its reference
// now.
jdr->clearReference();
signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
}
}
}

JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
: mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),// object -> DeathRecipient 变为 GC root
mObjectWeak(NULL), mList(list)
{
// These objects manage their own lifetimes so are responsible for final bookkeeping.
// The list holds a strong reference to this object.
LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
list->add(this);

gNumDeathRefsCreated.fetch_add(1, std::memory_order_relaxed);
gcIfManyNewRefs(env);
}

unlinkToDeath 最终会在 Jni 中 通过 env->DeleteGlobalRef(mObject),告诉虚拟机 AuthSession 不是GC root。


virtual ~JavaDeathRecipient()
{
//ALOGI("Removing death ref: recipient=%p\n", mObject);
gNumDeathRefsDeleted.fetch_add(1, std::memory_order_relaxed);
JNIEnv* env = javavm_to_jnienv(mVM);
if (mObject != NULL) {
env->DeleteGlobalRef(mObject);// object -> DeathRecipient GC root 被撤销
} else {
env->DeleteWeakGlobalRef(mObjectWeak);
}
}

解决方式


AuthSession 置空的时候调用 IBiometricServiceReceiver 的 unlinkToDeath 方法。


总结


以上梳理的其实就是 Binder 的造成的内存泄漏。


问题严重性来看,也不算什么大问题,因为调用 BiometricPrompt 的进程被杀,system 进程相关实例也就回收释放了。一般 app 也不太可能出现,常驻进程,而且还频繁调用手机认证的。


这里主要介绍了一种容易被忽略的内存泄漏,Binder.linktoDeath()。
Google issuetracker


参考资料


Binder | 对象的生命周期


作者:Jingle_zhang
来源:juejin.cn/post/7202066794299129914
收起阅读 »

别再忘了锁屏,几行代码写一个人走屏锁小工具

写在前面 之前在公司,毕竟是干安全的,部门有这么一个要求,被发现不锁屏的,请全部门喝奶茶。很不幸,我也出现过忘了锁屏然后被发现的情况。自此之后,我就形成了肌肉记忆,同时也对别人不锁屏很敏感。 为什么要强调锁屏呢?你也不想你的电脑被别人操作吧,也不想自己的信息被...
继续阅读 »

写在前面


之前在公司,毕竟是干安全的,部门有这么一个要求,被发现不锁屏的,请全部门喝奶茶。很不幸,我也出现过忘了锁屏然后被发现的情况。自此之后,我就形成了肌肉记忆,同时也对别人不锁屏很敏感。


为什么要强调锁屏呢?你也不想你的电脑被别人操作吧,也不想自己的信息被别人获取吧。毕竟防人之心不可无。


自打跳槽到新公司之后,每次去厕所的路上就看到有人电脑不锁屏,真的是令我无比的纠结。锁个屏幕有那么难吗?确实很难,有时候一忙就容易忘,于是我就想实现一个离开电脑自动锁屏的程序。


分析


这玩意实现也不难,简单思考一下,就是让电脑检测人在不在电脑前面,那就是要试试捕获摄像头了,然后设置一个间隔时间,每隔一段时间截取图片,做人脸识别,没有人脸了就锁屏就行了。


涉及到摄像头图片处理,直接让人联想到opencv,然后再用python实现上面的一套逻辑,就搞定。


代码


安装opencv的库


pip install opencv-python

直接上代码:


import cv2
import time
import os
import platform

# 检测操作系统
def detect_os():
os_name = platform.system()
if os_name == 'Windows':
return 'windows'
elif os_name == 'Darwin':
return 'mac'
else:
return 'other'

# 执行锁屏命令
def lock_screen(os_type):
if os_type == 'windows':
os.system('rundll32.exe user32.dll, LockWorkStation')
elif os_type == 'mac':
os.system('/System/Library/CoreServices/"Menu Extras"/User.menu/Contents/Resources/CGSession -suspend')


# 初始化摄像头
cap = cv2.VideoCapture(0)

# 载入OpenCV的人脸检测模型
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

# 无人状态计时器
no_person_timer = 0
# 设定无人状态时间阈值
NO_PERSON_THRESHOLD = 3

# 检测操作系统类型
os_type = detect_os()

while True:
ret, frame = cap.read()
if not ret:
break

# 转换为灰度图像
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.1, 4)

if len(faces) == 0:
no_person_timer += 1
else:
no_person_timer = 0

# 如果超过阈值,则锁屏
if no_person_timer > NO_PERSON_THRESHOLD:
lock_screen(os_type)
no_person_timer = 0

time.sleep(1)

cap.release()

代码里都做好了注释,很简单,因为windows和macOS的锁屏指令不一样,所以做了个简单的系统平台判断。


可以完美执行,就是它得一直调用摄像头,应该也不会有人真的使用这玩意吧,hhh。


作者:银空飞羽
来源:juejin.cn/post/7317824480911458304
收起阅读 »

wordcloud,一个超酷的python库

微信公众号:愤怒的it男,超多Python技术干货文章。 一、简单介绍一下 词云图是文本挖掘中用来表征词频的数据可视化图像,通过它可以很直观地展现文本数据中地高频词,让读者能够从大量文本数据中快速抓住重点。如下图: wordcloud则是一个非常优秀的词云...
继续阅读 »

微信公众号:愤怒的it男,超多Python技术干货文章。



一、简单介绍一下


词云图是文本挖掘中用来表征词频的数据可视化图像,通过它可以很直观地展现文本数据中地高频词,让读者能够从大量文本数据中快速抓住重点。如下图:


图1.png


wordcloud则是一个非常优秀的词云展示python库,它支持自定义词云图的大小、颜色、字体等,甚至可以通过蒙版图片设置词云图的形状。因此,我们可以借助wordcloud轻松生成精美的词云图。


二、安装只需一行命令


pip install wordcloud

三、从一个简单例子开始


from wordcloud import WordCloud

text = "微信公众号:愤怒的it男"

wc = WordCloud(font_path='FZYTK.TTF', repeat=True)
wc.generate(text)
wc.to_file('wordcloud.png')

这里通过WordCloud类设置字体为方正姚体,背景颜色为白色,文本可以重复显示。生成WordCloud对象后,使用generate()方法将“微信公众号:愤怒的it男”生成词云图。最后,使用to_file()方法生成图片文件。


图2.png


四、细说wordcloud


WordCloud作为wordcloud库最核心的类,其主要参数及说明如下:


图3.PNG


这里以wordcloud库官方文档的constitution.txt文件作为数据,覆盖WordCloud类的各种参数设置用法,绘制出一张精美的词云图。


图4.PNG


首先,读入constitution.txt数据,并将数据清洗成空格分隔的长字符串。


import re

with open('constitution.txt') as c:
text = ' '.join([word.group().lower() for word in re.finditer('[a-zA-Z]+', c.read())])

print(text[:500])

图5.PNG


然后,在默认参数设置下,使用WordCloud对象的generate()和to_file()方法生成一张简单的词云图。


from wordcloud import WordCloud
import re

with open('constitution.txt') as c:
text = ' '.join([word.group().lower() for word in re.finditer('[a-zA-Z]+', c.read())])

wc = WordCloud()
wc.generate(text)

wc.to_file('wordcloud.png')

图6.png


以上词云图是在默认参数下生成的,简单粗糙不好看。接下来我们将对WordCloud的各种参数调整设置,不断地对以上词云图进行升级改造。


1、设置图片属性


设置图片宽为600,高为300,放大1.5倍,色彩空间为RGBA,背景颜色为。


from wordcloud import WordCloud
import re

with open('constitution.txt') as c:
text = ' '.join([word.group().lower() for word in re.finditer('[a-zA-Z]+', c.read())])

wc = WordCloud(
width=600,
height=300,
scale=1.5,
mode='RGBA',
background_color=,
)
wc.generate(text)

wc.to_file('wordcloud.png')

图7.png


2、设置文字布局


设置水平比例为1(即全部为水平文字),最多只显示100个词,停用词使用自带的词典(中文需要传入自定义的),相关一致性为0.3,文字布局为非随机,不允许重复词。


from wordcloud import WordCloud
import re

with open('constitution.txt') as c:
text = ' '.join([word.group().lower() for word in re.finditer('[a-zA-Z]+', c.read())])

wc = WordCloud(
width=600,
height=300,
scale=1.5,
mode='RGBA',
background_color=,
prefer_horizontal=1,
max_words=400,
stopwords=,
relative_scaling=0.3,
random_state=4,
repeat=False,
)
wc.generate(text)

wc.to_file('wordcloud.png')

图8.png


3、设置字体属性


设置字体为‘JOKERMAN.TTF’,最小字号为2,最大字号为150。


from wordcloud import WordCloud
import re

with open('constitution.txt') as c:
text = ' '.join([word.group().lower() for word in re.finditer('[a-zA-Z]+', c.read())])

wc = WordCloud(
width=600,
height=300,
scale=1.5,
mode='RGBA',
background_color=,
prefer_horizontal=1,
max_words=400,
stopwords=,
relative_scaling=0.3,
random_state=4,
repeat=False,
font_path='JOKERMAN.TTF',
min_font_size=2,
max_font_size=150,
)
wc.generate(text)

wc.to_file('wordcloud.png')

图9.png


4、设置蒙版


图10.PNG


设置微信公众号【愤怒的it男】头像的黑白图片为蒙版图片。


from PIL import Image
from wordcloud import WordCloud
import numpy as np
import re

mask_picture = np.array(Image.open('angry_it_man_mask.png'))

with open('constitution.txt') as c:
text = ' '.join([word.group().lower() for word in re.finditer('[a-zA-Z]+', c.read())])

wc = WordCloud(
width=600,
height=300,
scale=1.5,
mode='RGBA',
background_color=,
prefer_horizontal=1,
max_words=400,
stopwords=,
relative_scaling=0.3,
random_state=4,
repeat=False,
font_path='JOKERMAN.TTF',
min_font_size=2,
max_font_size=150,
mask=mask_picture,
)
wc.generate(text)

wc.to_file('wordcloud.png')

图11.png



微信公众号:愤怒的it男,超多Python技术干货文章。



作者:愤怒的it男
来源:juejin.cn/post/7317214007572807731
收起阅读 »

Python中级知识梳理

1. 文件操作 Python中的文件操作通常使用内置的open()函数来打开文件。以下是一个简单的示例: with open("file.txt", "r") as f: content = f.read() print(content) 在...
继续阅读 »

image.png


1. 文件操作


Python中的文件操作通常使用内置的open()函数来打开文件。以下是一个简单的示例:


with open("file.txt", "r") as f:
content = f.read()
print(content)

在这个示例中,我们打开了名为"file.txt"的文件,并将其读入变量content中,最后将其打印出来。


open()函数的第一个参数是文件名,第二个参数是打开文件的模式。以下是一些常用的模式:



  • "r":只读模式

  • "w":写入模式(会覆盖已有文件)

  • "a":追加模式(不会覆盖已有文件)


2. 正则表达式


正则表达式是一种强大的工具,可以帮助我们从文本中提取信息或进行文本替换。Python中可以使用内置的re模块来进行正则表达式操作。以下是一个示例:


import re

text = "The quick brown fox jumps over the lazy dog."
pattern = r"fox"
matches = re.findall(pattern, text)
print(matches)

在这个示例中,我们定义了一个正则表达式模式r"fox",然后使用re.findall()函数来查找匹配该模式的所有字符串。最后,我们将匹配的结果打印出来。


3. 异常处理


在编写程序时,经常需要处理可能出现的错误或异常情况。Python中可以使用tryexcept语句来实现异常处理。以下是一个简单的示例:


try:
x = 1 / 0
except ZeroDivisionError:
print("Error: division by zero")

在这个示例中,我们尝试计算1除以0,这将引发一个ZeroDivisionError异常。我们使用tryexcept语句来捕获该异常并打印出一条错误消息。


4. 面向对象编程(Object-Oriented Programming)


面向对象编程是一种重要的编程范式,Python是一种面向对象的语言。以下是一个简单的示例:


class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def say_hello(self):
print("Hello, my name is " + self.name)

person = Person("Alice", 25)
person.say_hello()

在这个示例中,我们定义了一个名为Person的类,并定义了一个构造函数__init__()来初始化对象的属性。我们还定义了一个名为say_hello()的方法,用于打印出问候语。最后,我们创建一个Person对象,并调用say_hello()方法。
Python是一种面向对象编程语言,它支持面向对象编程的三个基本概念:封装、继承和多态。面向对象编程的核心思想是将数据和操作数据的方法封装在一起,形成一个对象。


示例代码


class Animal:
def __init__(self, name, age):
self.name = name
self.age = age

def say_hello(self):
print(f'{self.name} is saying hello')

class Cat(Animal):
def __init__(self, name, age, color):
super().__init__(name, age)
self.color = color

def catch_mouse(self):
print(f'{self.name} is catching mouse')

cat = Cat('Tom', 2, 'White')
cat.say_hello()
cat.catch_mouse()

输出结果


Tom is saying hello
Tom is catching mouse

5. Python高级特性


5.1 生成器(Generator)


生成器是一种特殊的函数,可以在函数执行期间多次返回值,并且可以保留当前执行状态,等待下一次调用。它们是高效的迭代器,可以用于处理大量数据或无限流数据,同时也能节省内存。


示例代码


pythonCopy code
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a + b

f = fib()
for i in range(10):
print(next(f))

输出结果


Copy code
0
1
1
2
3
5
8
13
21
34

5.2 装饰器(Decorator)


装饰器是一种函数,用于修改其他函数的行为。它们提供了一种简单的方式来修改函数,而无需修改函数的原始定义。装饰器的常见用途包括添加日志、计时、缓存等功能。


示例代码


pythonCopy code
import time

def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'{func.__name__} executed in {end - start} seconds')
return result
return wrapper

@timer
def calculate_sum(n):
return sum(range(n+1))

print(calculate_sum(100000000))

输出结果


Copy code
calculate_sum executed in 4.150076866149902 seconds
5000000050000000

作者:SandySY
来源:juejin.cn/post/7224335234010234935
收起阅读 »

你的flask服务开启https了吗?

一、你的flask服务开启https了吗? 1.事件起因 计划做文心一言插件,我购买了服务器,开始按照我的想象部署插件,结果工作不错,但是最后一步图片显示不正常。哭晕了,如下图所示。 仔细阅读,发现返回图片地址什么的都很正常啊,也可以访问得到,但是为什么就不...
继续阅读 »

一、你的flask服务开启https了吗?


1.事件起因


计划做文心一言插件,我购买了服务器,开始按照我的想象部署插件,结果工作不错,但是最后一步图片显示不正常。哭晕了,如下图所示。


e3dde086617a68f9a585f78f8bfdc20.png
仔细阅读,发现返回图片地址什么的都很正常啊,也可以访问得到,但是为什么就不能看到呢,很奇怪。


经多方排查,最终确定是图片跨域导致无法显示。


2.解决思路


知道是跨域问题就好了,因为文心一言是https访问,所以提供服务的也需要https,那么就开始作了(解决)。


二、解决办法


1.无效1.0解决办法


知道要https那我就加https了,直接百度解决办法。



  • Flask(更具体地说其实是Werkzeug),支持使用即时证书,这对于通过HTTPS快速提供应用程序非常有用,而且不会搞乱系统的证书。你只有需要做的就是将 ssl_context ='adhoc' 添加到程序的 app.run() 调用中。遗憾的是,Flask CLI无法使用此选项。举个例子,下面是官方文档中的“Hello,World” Flask应用程序,并添加了TLS加密:

  • 安装库 pip install pyopenssl


from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
return "Hello World!"

if __name__ == "__main__":
app.run(ssl_context='adhoc')


这样启动起来就是https了,但是访问问题依旧,图片仍然没有显示。。。。。。仔细看来浏览器说是假的ssl,那就继续解决。


2.无效2.0自签名证书解决办法


所谓的自签名证书是使用与同一证书关联的私钥生成签名的证书,就是自己动手丰衣足食。


微信截图_20231126163515.png



  • 生成证书

  • flask加载证书


openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
return "Hello World!"

if __name__ == "__main__":
app.run(ssl_context=('cert.pem', 'key.pem'))
复制代码

然后看结果,依然无效,因为是自签名。。。。。。


3.0终极解决办法


后来发现要真正的证书,那么你必须要有域名,才会发给你,就是说证书和域名是绑定的,就跟户籍一样,户籍都没有说什么学区房,没人理你。



  • 为此我花了8块买了一个cn一年的域名,并且进行了实名。

  • 在腾讯云申请ssl证书,参考地址 cloud.tencent.com/document/pr…

  • 申请地址 console.cloud.tencent.com/ssl
    申请时必须先证明该证书属于你,需要按提示加入cname,进行验证。申请完毕略等一会就会通过,下载证书即可,具体包含以下几个问题件:


微信截图_20231126164111.png



  • 加载证书
    因为有4个文件,没有详细写,因此我测试了几次,最终成功。


if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', ssl_context=( 'erniebotplugins.cn_bundle.crt','erniebotplugins.cn.key'), port=8081)

三、最终效果


用上最终大招后,最终成功,具体效果如下。


微信截图_20231126164250.png


可见现在ssl非常普遍,不安全别的网站都懒得搭理你。


作者:Livingbody
来源:juejin.cn/post/7305235970285568011
收起阅读 »

如何实现一个下班倒计时程序

Hello伙伴们,好几天不见啦。最近也是晚上打球太累,加上一直在研究mybatis的多租户问题,简直是没有太多的精力了。正好周六的晚上有一点点的空隙,就是洗完澡之后,顿时觉得整个人轻松下来了。有伙伴跟我一样的感受吗? 话不多说,现在我们来开始今天的主题:《如何...
继续阅读 »

Hello伙伴们,好几天不见啦。最近也是晚上打球太累,加上一直在研究mybatis的多租户问题,简直是没有太多的精力了。正好周六的晚上有一点点的空隙,就是洗完澡之后,顿时觉得整个人轻松下来了。有伙伴跟我一样的感受吗?


话不多说,现在我们来开始今天的主题:《如何实现一个桌面倒计时程序》。


身为打工人,一定是想着下班的那一刻吧。就像我昨天和我的伙伴开玩笑说:一个月就盼望着发工资的那一天。shigen找到了一段程序来实现下班倒计时,一起来看看实现的效果吧:


倒计时应用


页面上动态的显示当前时间和剩余时间,假设shigen的文章要在今天的23点写完,那么我还剩2小时25分钟的准备时间。是不是挺神奇的,另外,还可以实现到点了自动关机。人走电脑关,看看老板还有什么理由让你去加班🤫🤫🤫。


那就上今天的代码吧:


 # -*- encoding: utf-8 -*-
 __date__ = '2023/11/18 19:27:08'
 
 """
 距离下班时间倒计时
 """

 
 import time
 from tkinter import *
 
 
 def refresh_current_time():
     """刷新当前时间"""
     clock_time = time.strftime('%Y-%m-%d %H:%M:%S')
     curr_time.config(text=clock_time)
     curr_time.after(1000, refresh_current_time)
 
 
 def refresh_down_time():
     """刷新倒计时时间"""
     # 当前时间戳
     now_time = int(time.time())
     # 下班时间时分秒数据过滤
     work_hour_val = int(work_hour.get())
     if work_hour_val > 23:
         down_label.config(text='小时的区间为(00-23)')
         return
     work_minute_val = int(work_minute.get())
     if work_minute_val > 59:
         down_label.config(text='分钟的区间为(00-59)')
         return
     work_second_val = int(work_second.get())
     if work_second_val > 59:
         down_label.config(text='秒数的区间为(00-59)')
         return
     # 下班时间转为时间戳
     work_date = str(work_hour_val) + ':' + str(work_minute_val) + ':' + str(work_second_val)
     work_str_time = time.strftime('%Y-%m-%d ') + work_date
     time_array = time.strptime(work_str_time, "%Y-%m-%d %H:%M:%S")
     work_time = time.mktime(time_array)
     if now_time > work_time:
         down_label.config(text='已过下班时间')
         return
     # 距离下班时间剩余秒数
     diff_time = int(work_time - now_time)
     while diff_time > -1:
         # 获取倒计时-时分秒
         down_minute = diff_time // 60
         down_second = diff_time % 60
         down_hour = 0
         if down_minute > 60:
             down_hour = down_minute // 60
             down_minute = down_minute % 60
         # 刷新倒计时时间
         down_time = str(down_hour).zfill(2) + '时' + str(down_minute).zfill(2) + '分' + str(down_second).zfill(2) + '秒'
         down_label.config(text=down_time)
         tk_obj.update()
         time.sleep(1)
         if diff_time == 0:
             # 倒计时结束
             down_label.config(text='已到下班时间')
             # 自动关机,定时一分钟关机,可以取消
             # down_label.config(text='下一分钟将自动关机')
             # os.system('shutdown -s -f -t 60')
             break
         diff_time -= 1
 
 
 # 程序主入口
 if __name__ == "__main__":
     # 设置页面数据
     tk_obj = Tk()
     tk_obj.geometry('400x280')
     tk_obj.resizable(0, 0)
     tk_obj.config(bg='white')
     tk_obj.title('倒计时应用')
     Label(tk_obj, text='下班倒计时', font='宋体 20 bold', bg='white').pack()
     # 设置当前时间
     Label(tk_obj, font='宋体 15 bold', text='当前时间:', bg='white').place(x=50, y=60)
     curr_time = Label(tk_obj, font='宋体 15', text='', fg='gray25', bg='white')
     curr_time.place(x=160, y=60)
     refresh_current_time()
     # 设置下班时间
     Label(tk_obj, font='宋体 15 bold', text='下班时间:', bg='white').place(x=50, y=110)
     # 下班时间-小时
     work_hour = StringVar()
     Entry(tk_obj, textvariable=work_hour, width=2, font='宋体 12').place(x=160, y=115)
     work_hour.set('18')
     # 下班时间-分钟
     work_minute = StringVar()
     Entry(tk_obj, textvariable=work_minute, width=2, font='宋体 12').place(x=185, y=115)
     work_minute.set('00')
     # 下班时间-秒数
     work_second = StringVar()
     Entry(tk_obj, textvariable=work_second, width=2, font='宋体 12').place(x=210, y=115)
     work_second.set('00')
     # 设置剩余时间
     Label(tk_obj, font='宋体 15 bold', text='剩余时间:', bg='white').place(x=50, y=160)
     down_label = Label(tk_obj, font='宋体 23', text='', fg='gray25', bg='white')
     down_label.place(x=160, y=155)
     down_label.config(text='00时00分00秒')
     # 开始计时按钮
     Button(tk_obj, text='START', bd='5', command=refresh_down_time, bg='green', font='宋体 10 bold').place(x=150, y=220)
     tk_obj.mainloop()

代码就是简简单单的204行,要实现到点自动关机的伙伴可以把63-64行的代码注释打开即可。


那最后总结一下吧,为什么shigen会选取这个程序作为今天的分享呢?



  1. 跨平台。首先python是跨平台的,其次tkinter也是跨平台的,意味着在常见的操作系统都可以执行这个代码,实现倒计时的效果;

  2. 新思路。其实shigen之前也做了一个类似的桌面时钟效果,做的更加酷炫一点的话,其实可以当作屏保了;

  3. 小工具的改造。其实shigen的mac上也有很多的小工具,但是都是在命令行执行的,改在了GUI界面岂不是更加的nice和方便,也实现傻瓜式操作。

作者:shigen01
来源:juejin.cn/post/7302348032543899660
收起阅读 »

Python枚举类:定义、使用和最佳实践

枚举(Enum)是一种有助于提高代码可读性和可维护性的数据类型,允许我们为一组相关的常量赋予有意义的名字。 在Python中,枚举类(Enum)提供了一种简洁而强大的方式来定义和使用枚举。 一、枚举类 1.1 什么是枚举类? 枚举类是一种特殊的数据类型,用于表...
继续阅读 »


枚举(Enum)是一种有助于提高代码可读性和可维护性的数据类型,允许我们为一组相关的常量赋予有意义的名字。


在Python中,枚举类(Enum)提供了一种简洁而强大的方式来定义和使用枚举。


一、枚举类


1.1 什么是枚举类?


枚举类是一种特殊的数据类型,用于表示一组具有离散取值的常量。它将常量与有意义的名字关联起来,使得代码更易读、更易维护。枚举类的每个成员都有一个唯一的名称和一个关联的值。


枚举类的典型用例包括表示颜色、方向、状态、星期几等常量值。使用枚举可以增强代码的可读性,减少硬编码的风险。


1.2 Python中的枚举类


在Python中,使用内置模块enum来创建和使用枚举类。


enum模块提供了Enum类,允许定义自己的枚举类型。


二、定义和使用枚举类


2.1 定义枚举类


要定义一个枚举类,需要导入Enum类并创建一个继承自它的子类。在子类中,我们定义枚举成员,并为每个成员分配一个名称和一个关联的值。


示例代码:


from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

在这个示例中,定义一个名为Color的枚举类,它有三个成员:REDGREENBLUE,每个成员都有一个整数值与之关联。


2.2 访问枚举成员


定义枚举类,可以通过成员名来访问枚举成员。例如:


print(Color.RED)    # 输出:Color.RED
print(Color.GREEN)  # 输出:Color.GREEN

2.3 获取枚举成员的值


要获取枚举成员的关联值,可以使用成员的value属性。例如:


print(Color.RED.value)    # 输出:1
print(Color.GREEN.value)  # 输出:2

2.4 比较枚举成员


枚举成员可以使用相等运算符进行比较。可以直接比较枚举成员,而不必比较它们的值。例如:


color1 = Color.RED
color2 = Color.GREEN

print(color1 == color2)  # 输出:False

2.5 迭代枚举成员


使用for循环来迭代枚举类的所有成员。例如,要打印所有颜色的名称和值:


for color in Color:
    print(f"{color.name}: {color.value}")

2.6 将值映射到枚举成员


根据枚举成员的值来获取成员本身,可以通过枚举类的__members__属性来实现。


例如,要根据值获取Color枚举成员:


value = 2
color = Color(value)
print(color)  # 输出:Color.GREEN

三、枚举的最佳实践


第三部分:枚举的最佳实践


枚举是一种有用的数据类型,但在使用时需要遵循一些最佳实践,以确保代码的可读性和可维护性。


3.1 使用枚举代替魔术数字


在代码中使用枚举来代替魔术数字(不明确的常量值)可以增加代码的可读性。枚举为常量提供了有意义的名字,使得代码更容易理解。


例如,使用枚举来表示星期几:


from enum import Enum

class Weekday(Enum):
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 4
    FRIDAY = 5
    SATURDAY = 6
    SUNDAY = 7

3.2 避免硬编码


尽量避免在代码中硬编码枚举成员的值。如果需要使用枚举成员的值,最好使用枚举成员本身而不是其值。这可以提高代码的可读性,使得代码更容易维护。


例如,避免这样的写法:


if day == 1:  # 避免硬编码
    print("Today is Monday")

而使用枚举成员:


if day == Weekday.MONDAY:  # 更具表现力
    print("Today is Monday")

3.3 使用枚举成员的名称


枚举成员的名称通常应该使用大写字母,以便与常规变量和函数名称区分开。这是一种约定,有助于提高代码的可读性。例如,使用RED而不是red


3.4 考虑枚举成员的值类型


枚举成员的值通常是整数,但根据上下文和需求,可以选择不同的值类型,如字符串。选择适当的值类型可以使代码更具表现力。


3.5 考虑用法和上下文


在定义枚举时,考虑其用法和上下文。命名枚举成员和选择合适的值应该反映其在应用程序中的含义和用途。这有助于其他开发人员更容易理解和使用枚举。


3.6 枚举的不可变性


枚举成员是不可变的,一旦创建就不能更改其值。这有助于确保枚举成员的稳定性,并防止意外的修改。


遵循这些最佳实践可以帮助你有效地使用枚举,提高代码的可读性和可维护性。枚举是一种强大的工具,可以在代码中代替魔术数字,并提供有意义的常量名称。


总结


Python的枚举类是一种强大的工具,用于表示一组相关的常量,并提高代码的可读性和可维护性。通过枚举,我们可以为常量赋予有意义的名称,避免硬编码的值,以及更容易进行比较和迭代。


在实际编程中,枚举类可以提供一种清晰、可维护且更具表现力的方式来处理常量值。


作者:涛哥聊Python
来源:juejin.cn/post/7294150742112895002
收起阅读 »

136. 只出现一次的数字

题目 题解 考察的是位运算 —— 异或(^),相同为 0,不同为 1 1^0 = 1,1^1 = 0 则直接对数据所有元素执行 ^ 操作,最终的就是结果 class&nbs...
继续阅读 »

题目





题解





  • 考察的是位运算 —— 异或(^),相同为 0,不同为 1



  • 1^0 = 1,1^1 = 0



  • 则直接对数据所有元素执行 ^ 操作,最终的就是结果


class Solution {
    public int singleNumber(int[] nums) {

        int res = 0;

        for (int num : nums) {
            res = res ^ num;
        }

        return res;
    }
}

作者:程序员小航
来源:mdnice.com/writing/7bb65e0150154b28b4777a1ea6e2784b
收起阅读 »

【JD京东抢购】茅台抢购逻辑

直接进入正文。京东抢购模式有很多种。 普通商品无货,定时查询库存蹲抢普通商品定时发售(库存由0变为有货),定时提前构造订单请求抢预售商品(需要先预约),可以加入购物车,通过购物车结算。这种用常规购物车结算订单接口就行,当然也可以用抢购接口。 这种体现为可以加...
继续阅读 »

直接进入正文。京东抢购模式有很多种。


  1. 普通商品无货,定时查询库存蹲抢
  2. 普通商品定时发售(库存由0变为有货),定时提前构造订单请求抢
  3. 预售商品(需要先预约),可以加入购物车,通过购物车结算。这种用常规购物车结算订单接口就行,当然也可以用抢购接口。


这种体现为可以加购,抢购时候显示两个按钮,加入购物车(黄色)和立即购买(淡绿色)。



  • 预售商品(需要先预约),无法加入购物车,电脑端无法预约,必须手机端预约。这种采用marathon.jd.com/seckillnew/… 接口完成抢购,有完整流程验证和tokenKey(sign),sk验证。


这种体现为 无法加入购物车,必须手机端才能预约,可购买时候只显示一个红色按钮立即抢购



逻辑参考GitHub大佬给出的思路。


第一步:获取跳转链接


跳转链接是指形如:un.m.jd.com/cgi-bin/app… 的链接,获取该链接,还需要一个前置步骤,即获取token和拼接url。先说获取token,获取token是通过genToken接口获取的,然后将获取到的tokenKey和url拼接起来,得到跳转链接。


第二步:访问跳转链接


拿到跳转链接后,直接将该跳转链接仍给浏览器即可,浏览器会经过两次302跳转得到sekill.action链接,从而渲染出提交订单页面,此时我们需要模拟点击“提交订单”按钮,实现抢购。(可以使用Selenium、Pyppeteer或Playwright等类库 来模拟浏览器)


访问跳转连接,及提交订单的时候需要提供移动端的APP参数抓包获取。Android抓包较为简单,IOS的也不麻烦,就是步骤多了一些。


然后提取Hades头的信息组成以下参数

        query_params = {
"functionId": "genToken",
"clientVersion": "12.0.8",
"build": "168782",
"client": "apple",
"d_brand": "apple",
"d_model": "iPhone11,4",
"osVersion": "16.5",
"screen": "1284*2778",
"partner": "apple",
"aid": self.aid,
"eid": self.eid,
"sdkVersion": "29",
"lang": "zh_CN",
# 'harmonyOs': '0',
"uuid": self.uuid,
"area": "4_51026_58465_0",
"networkType": "wifi",
"wifiBssid": self.wifiBssid,
"uts": self.uts,
"uemps": "0-0-0",
"ext": '{"prstate":"0","pvcStu":"1"}',
# 'ef': '1',
# 'ep': json.dumps(ep, ensure_ascii=False, separators=(',', ':')),
}

这种仅仅是前面所需的参数。具体方法还是需要使用这些参数来获取用户的个人信息拿到跳转连接


如:

    def get_appjmp(self, token_params):
headers = {"user-agent": self.ua}
appjmp_url = token_params["url"]
params = {
"to": "https://divide.jd.com/user_routing?skuId=%s" % self.skuId,
"tokenKey": token_params["tokenKey"],
}

response = self.s.get(
url=appjmp_url,
params=params,
allow_redirects=False,
verify=False,
headers=headers,
)
print("Get Appjmp跳转链接-------------->%s" % response.headers["Location"])
return response.headers["Location"]

get_appjmp(self, token_params) 函数接受一个名为 token_params 的参数。


然后发送相关请求后携带参数得到跳转链接。

  • headers 是一个字典,包含了请求头中的 "User-Agent" 字段,用于模拟浏览器的用户代理。

  • appjmp_url 是一个变量,它存储了 token_params 字典中的 "url" 键所对应的值。

  • params 是一个字典,其中包含两个键值对:

  • "to" 键对应的值是一个字符串,使用了 %s 占位符,用于生成跳转链接中的 skuId 参数。

  • "tokenKey" 键对应的值是一个字符串,使用了 token_params 字典中的 "tokenKey" 键所对应的值。

  • 通过调用 self.s.get() 方法发起一个 GET 请求,传入以下参数:

  • url 参数是 appjmp_url,表示要访问的链接地址。

  • params 参数是之前定义的 params 字典,用于添加请求参数。

  • allow_redirects 参数设置为 False,禁止自动重定向。

  • verify 参数设置为 False,跳过 SSL 证书验证。

  • headers 参数是之前定义的 headers 字典,用于设置请求头。

  • 最后,打印获取到的跳转链接的响应头中的 "Location" 字段值,并将其返回。


抢购返回解决无非就是



{'errorMessage': '很遗憾没有抢到,再接再厉哦。', 'orderId': 0, 'resultCode': 90016, 'skuId': 0, 'success': False}




{'errorMessage': '很遗憾没有抢到,再接再厉哦。', 'orderId': 0, 'resultCode': 90008, 'skuId': 0, 'success': False}





根据其他作者的推测 推测返回 90008 是京东的风控机制,代表这次请求直接失败,不参与抢购。

小白信用越低越容易触发京东的风控。


具体代码可参考GitHub地址


感谢GitHub作者@geeeeeeeek @jd-seckill等


作者:狗头大军之江苏分军
链接:https://juejin.cn/post/7280740005571362816
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

电视剧里的代码真能运行吗?

大家好,欢迎来到 Crossin的编程教室 ! 前几天,后台老有小伙伴留言“爱心代码”。这不是Crossin很早之前发过的内容嘛,怎么最近突然又被人翻出来了?后来才知道 ,原来是一部有关程序员的青春偶像剧《点燃我,温暖你》在热播,而剧中有一段关于期中考试要用程...
继续阅读 »

大家好,欢迎来到 Crossin的编程教室 !


前几天,后台老有小伙伴留言“爱心代码”。这不是Crossin很早之前发过的内容嘛,怎么最近突然又被人翻出来了?后来才知道


,原来是一部有关程序员的青春偶像剧《点燃我,温暖你》在热播,而剧中有一段关于期中考试要用程序画一个爱心的桥段。


于是出于好奇,Crossin就去看了这一集(第5集,不用谢)。这一看不要紧,差点把刚吃的鸡腿给喷出来--槽点实在太多了!


忍不住做了个欢乐吐槽向的代码解读视频,在某平台上被顶到了20个w的浏览,也算蹭了一波人家电视剧的热度吧……


下面是图文版,给大家分析下剧中出现的“爱心”代码,并且来复刻一下最后男主完成的酷炫跳动爱心。


剧中代码赏析


1. 首先是路人同学的代码:



虽然剧中说是“C语言期中考试”,但这位同学的代码名叫 draw2.py,一个典型的 Python 文件,再结合截图中的 pen.forward、pen.setpos 等方法来看,应该是用 turtle 海龟作图库来画爱心。那效果通常是这样的:


import turtle as t
t.color('red')
t.setheading(50)
t.begin_fill()
t.circle(-100, 170)
t.circle(-300, 40)
t.right(38)
t.circle(-300, 40)
t.circle(-100, 170)
t.end_fill()
t.done()



而不是剧中那个命令行下用1组成的不规则的图形。


2. 然后是课代表向路人同学展示的优秀代码:



及所谓的效果:



这确实是C语言代码了,但文件依然是以 .py 为后缀,并且 include 前面没有加上 #,这显然是没法运行的。


里面的内容是可以画出爱心的,用是这个爱心曲线公式:



然后遍历一个15*17的方阵,计算每个坐标是在曲线内还是曲线外,在内部就输出#或*,外部就是-


用python改写一下是这样的:


for y in range(9, -6, -1):
for x in range(-8, 9):
print('*##*'[(x+10)%4] if (x*x+y*y-25)**3 < 25*x*x*y*y*y else '-', end=' ')
print()

效果:



稍微改一下输出,还能做出前面那个全是1的效果:


for y in range(9, -6, -1):
for x in range(-8, 9):
print('1' if (x*x+y*y-25)**3 < 25*x*x*y*y*y else ' ', end=' ')
print()


但跟剧中所谓的效果相去甚远。


3. 最后是主角狂拽酷炫D炸天的跳动爱心:



代码有两个片段:




但这两个片段也不C语言,而是C++,且两段并不是同一个程序,用的方法也完全不一样。


第一段代码跟前面一种思路差不多,只不过没有直接用一条曲线,而是上半部用两个圆形,下半部用两条直线,围出一个爱心。



改写成 Python 代码:


size = 10
for x in range(size):
for y in range(4*size+1):
dist1 = ((x-size)**2 + (y-size)**2) ** 0.5
dist2 = ((x-size)**2 + (y-3*size)**2) ** 0.5
if dist1 < size + 0.5 or dist2 < size + 0.5:
print('V', end=' ')
else:
print(' ', end=' ')
print()

for x in range(1, 2*size):
for y in range(x):
print(' ', end=' ')
for y in range(4*size+1-2*x):
print('V', end=' ')
print()

运行效果:



第二段代码用的是基于极坐标的爱心曲线,是遍历角度来计算点的位置。公式是:



计算出不同角度对应的点坐标,然后把它们连起来,就是一个爱心。


from math import pi, sin, cos
import matplotlib.pyplot as plt
no_pieces = 100
dt = 2*pi/no_pieces
t = 0
vx = []
vy = []
while t <= 2*pi:
vx.append(16*sin(t)**3)
vy.append(13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t))
t += dt
plt.plot(vx, vy)
plt.show()

效果:



代码中循环时用到的2π是为了保证曲线长度足够绕一个圈,但其实长一点也无所谓,即使 π=100 也不影响显示效果,只是相当于同一条曲线画了很多遍。所以剧中代码里写下35位小数的π,还被女主用纸笔一字不落地抄写下来,实在是让程序员无法理解的迷惑行为。



但不管写再多位的π,上述两段代码都和最终那个跳动的效果差了五百只羊了个羊。


跳动爱心实现


作为一个总是在写一些没什么乱用的代码的编程博主,Crossin当然也不会放过这个机会,下面就来挑战一下用 Python 实现最终的那个效果。


1. 想要绘制动态的效果,必定要借助一些库的帮助,不然代码量肯定会让你感动得想哭。这里我们将使用之前 羊了个羊游戏 里用过的 pgzero 库。然后结合最后那个极坐标爱心曲线代码,先绘制出曲线上离散的点。


import pgzrun
from math import pi, sin, cos

no_p = 100
dt = 2*3/no_p
t = 0
x = []
y = []
while t <= 2*3:
x.append(16*sin(t)**3)
y.append(13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t))
t += dt

def draw():
screen.clear()
for i in range(len(x)):
screen.draw.filled_rect(Rect((x[i]*10+400, -y[i]*10+300), (4, 4)), 'pink')

pgzrun.go()


2. 把点的数量增加,同时沿着原点到每个点的径向加一个随机数,并且这个随机数是按照正态分布来的(半个正态分布),大概率分布在曲线上,向曲线内部递减。这样,就得到这样一个随机分布的爱心效果。


...
no_p = 20000
...
while t <= 2*pi:
l = 10 - abs(random.gauss(10, 2) - 10)
x.append(l*16*sin(t)**3)
y.append(l*(13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t)))
t += dt
...


3. 下面就是让点动起来,这步是关键,也有一点点复杂。为了方便对于每个点进行控制,这里将每个点自定义成了一个Particle类的实例。


从原理上来说,就是给每个点加一个缩放系数,这个系数是根据时间变化的正弦函数,看起来就会像呼吸的节律一样。


class Particle():
def __init__(self, pos, size, f):
self.pos = pos
self.pos0 = pos
self.size = size
self.f = f

def draw(self):
screen.draw.filled_rect(Rect((10*self.f*self.pos[0] + 400, -10*self.f*self.pos[1] + 300), self.size), 'hot pink')

def update(self, t):
df = 1 + (2 - 1.5) * sin(t * 3) / 8
self.pos = self.pos0[0] * df, self.pos0[1] * df

...

t = 0
def draw():
screen.clear()
for p in particles:
p.draw()

def update(dt):
global t
t += dt
for p in particles:
p.update(t)


4. 剧中爱心跳动时,靠中间的点波动的幅度更大,有一种扩张的效果。所以再根据每个点距离原点的远近,再加上一个系数,离得越近,系数越大。


class Particle():
...
def update(self, t):
df = 1 + (2 - 1.5 * self.f) * sin(t * 3) / 8
self.pos = self.pos0[0] * df, self.pos0[1] * df


5. 最后再用同样的方法画一个更大一点的爱心,这个爱心不需要跳动,只要每一帧随机绘制就可以了。


def draw():
...
t =
0
while t < 2*pi:
f = random.gauss(1.1, 0.1)
x = 16*sin(t)**3
y = 13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t)
size = (random.uniform(0.5,2.5), random.uniform(0.5,2.5))
screen.draw.filled_rect(Rect((10*f*x + 400, -10*f*y + 300), size), 'hot pink')
t += dt * 3


合在一起,搞定!



总结一下,就是在原本的基础爱心曲线上加上一个正态分布的随机量、一个随时间变化的正弦函数和一个跟距离成反比的系数,外面再套一层更大的随机爱心,就得到类似剧中的跳动爱心效果。


但话说回来,真有人会在考场上这么干吗?


除非真的是超级大学霸,不然就是食堂伙食太好--


吃太饱撑的……



代码已开源:https://gitee.com/crossin/easy-py/tree/master/221114%20%E7%88%B1%E5%BF%83%E4%BB%A3%E7%A0%81



作者:Crossin先生
来源:juejin.cn/post/7168388057631031332
收起阅读 »

当程序员纠结中午应该吃什么,那就用pygame来解决吧

写多了kotlin和android,最近想搞点小东西,于是拿出了长期没有宠爱的python,打算搞个小项目 想想应该写什么,对了,该吃饭了,诶,刚好,写一个能随机选择吃什么的小程序吧,只需要点击按钮,就会随机出现菜谱,然后再点一下,就会得出今天吃什么的结论 思...
继续阅读 »

写多了kotlin和android,最近想搞点小东西,于是拿出了长期没有宠爱的python,打算搞个小项目


想想应该写什么,对了,该吃饭了,诶,刚好,写一个能随机选择吃什么的小程序吧,只需要点击按钮,就会随机出现菜谱,然后再点一下,就会得出今天吃什么的结论


思路是这样的,读入一个txt文件,文件中写满想吃的东西,做到数据和代码区分,然后开始写UI,UI通过按钮点击随机展示美食即可


麻辣香锅
糖醋排骨
红烧肉
...

    import pygame
import random

class App:
   def __init__(self):
       # 初始化 Pygame
       pygame.init()

       # 创建窗口和设置标题
       self.window_size = (600, 300)
       self.window = pygame.display.set_mode(self.window_size)
       pygame.display.set_caption("What to Eat Today")

       # 设置字体对象
       self.font = pygame.font.Font('myfont.ttf', 32)

       # 加载菜单数据
       self.menu = []
       with open("menu.txt", "r") as f:
           for line in f:
               line = line.strip()
               if line != "":
                   self.menu.append(line)
               print(line) # 打印数据

if __name__ == "__main__":
   app = App()


运行一下


image-20230828201918635.png


nice,文件已经读入


这个时候的UI是一闪而过的,因为程序瞬间就执行完毕了,ok,那么我们就需要用一个循环维持UI窗口,然后设置开始选择按键,以及键盘控制按键,同时设置变量


today_menu表示今天吃的东西,


btn_start_stop表示按键文字,


cur_menu表示正处于随机中的美食,当我们按下开始按键时,cur_menu开始变换,当我们按下关闭按键时,cur_menu的数据就赋值到today_menu中,


show_wheel表示当前正处于随机中,还是已经结束了


只要增加一个无限循环,一切就会好起来的



       # 随机选择一道菜
       self.today_menu = ""
       self.btn_start_stop = "start"
       self.cur_menu = ""

       # 游戏主循环
       self.running = True
       self.show_wheel = False


       # 开关程序
       while self.running:
           for event in pygame.event.get():
               if event.type == pygame.QUIT:
                   self.running = False
               elif event.type == pygame.MOUSEBUTTONDOWN:
               ...

               # 增加一个elif 按键s,show_wheel为true, 按下q, show_wheel为false
               elif event.type == pygame.KEYDOWN:
               ...

运行结果


image-20230828202631700.png


现在已经有了窗口,接下来需要在上面画东西了


所用到的就是draw函数



   def draw(self):
       # 绘制界面
       self.window.fill((255, 255, 255))

       # 绘制菜单
       menu_surface = self.font.render(f"Today's Menu: {self.today_menu}", True, (0, 0, 0))
       ...

       # 绘制按钮
       button_size = min(self.window_size[0] // 4, self.window_size[1] // 4)
    ...

       btn_start = self.font.render(f"{self.btn_start_stop}", True, (0, 0, 0))
        # 缩小start 文字字号 以适应按钮
       btn_start = pygame.transform.scale(btn_start, (button_size // 3 * 2, button_size // 3 * 2))
       self.window.blit(btn_start, (button_x + button_size // 2 - btn_start.get_width() // 2, button_y + button_size // 2 - btn_start.get_height() // 2))

       # 滚轮动画
       ...
       pygame.display.update()

运行


image-20230828202741990.png


上面的代码仅仅能够展示一个静态的页面,


虽然界面平平无奇,似乎只有两行字?不然,实际上暗藏玄🐔,只要我们加上这段,



       # 绘制滚轮动画
       if self.show_wheel:
           wheel_size = min(self.window_size) // 2
           wheel_x = self.window_size[0] // 2 - wheel_size // 2
           wheel_y = self.window_size[1] // 2 - wheel_size // 2
           wheel_rect = pygame.Rect(wheel_x, wheel_y, wheel_size, wheel_size)
...

           # 随机选择并显示菜谱
           menu_index = random.randint(0, len(self.menu) - 1)
           menu_surface = self.font.render(self.menu[menu_index], True, (0, 0, 0))
           self.window.blit(menu_surface, (wheel_x + wheel_size // 2 - menu_surface.get_width() // 2, wheel_y + wheel_size // 2 - menu_surface.get_height() // 2))
           self.cur_menu = self.menu[menu_index]

当我们点击“start"


QQ20230828-203131-HD.gif


发现中间的菜谱动了起来,动了起来!都是大家爱吃的,只需要点击右边的stop就可以固定结果!


真正麻烦的在于那个滚轮动画,可以想见,我们需要额外的一个无限循环,每一帧都要修改cur_menu,同时更新动画中的菜谱,然后点击stop后,将cur_menu赋值给到today_menu,最麻烦的不是这些逻辑,而是位置,滚轮动画的位置设置为窗口正中间,然后画了两条线,看起来更好看,有一种,狙击枪瞄准的感觉


最后,进行简单优化,比如设置定时关闭等,全代码如下,如果你也不知道吃什么,就用这段代码 + 在同目录自定义一个txt文件,把自己想吃的写上去吧



import pygame
import random

class App:
   def __init__(self):
       # 初始化 Pygame
       pygame.init()

       # 创建窗口和设置标题
       self.window_size = (600, 300)
       self.window = pygame.display.set_mode(self.window_size)
       pygame.display.set_caption("What to Eat Today")

       # 设置字体对象
       self.font = pygame.font.Font('myfont.ttf', 32)

       # 加载菜单数据
       self.menu = []
       with open("menu.txt", "r") as f:
           for line in f:
               line = line.strip()
               if line != "":
                   self.menu.append(line)

       # 随机选择一道菜
       self.today_menu = ""
       self.btn_start_stop = "start"
       self.cur_menu = ""

       # 游戏主循环
       self.running = True
       self.show_wheel = False
       self.wheel_count = 0     # 记录滚轮动画播放的帧数


       # 开关程序
       while self.running:
           for event in pygame.event.get():
               if event.type == pygame.QUIT:
                   self.running = False
               elif event.type == pygame.MOUSEBUTTONDOWN:
                   if not self.show_wheel:
                       self.show_wheel = True
                       self.wheel_count = 0  # 点击按钮后重置计数器为0
                       self.btn_start_stop = "stop"
                   else:
                       self.show_wheel = False
                       self.today_menu = self.cur_menu  # 点击停止赋值
                       self.btn_start_stop = "start"

               # 增加一个elif 按键s,show_wheel为true, 按下q, show_wheel为false
               elif event.type == pygame.KEYDOWN:
                   if event.key == pygame.K_s:  # 按下 s 键
                       self.show_wheel = True
                       self.wheel_count = 0  # 重置计数器为0
                   elif event.key == pygame.K_q:  # 按下 q 键
                       self.show_wheel = False
                       self.today_menu = self.cur_menu  # 停止赋值

           self.draw()

   def draw(self):
       # 绘制界面
       self.window.fill((255, 255, 255))

       # 绘制菜单
       menu_surface = self.font.render(f"Today's Menu: {self.today_menu}", True, (0, 0, 0))
       menu_x = self.window_size[0] // 2 - menu_surface.get_width() // 2
       menu_y = self.window_size[1] // 2 - menu_surface.get_height() // 2
       self.window.blit(menu_surface, (menu_x, menu_y))

       # 绘制按钮
       button_size = min(self.window_size[0] // 4, self.window_size[1] // 4)
       button_x = self.window_size[0] - button_size - 20
       button_y = self.window_size[1] - button_size - 20
       button_rect = pygame.Rect(button_x, button_y, button_size, button_size)
       pygame.draw.circle(self.window, (255, 0, 0), (button_x + button_size // 2, button_y + button_size // 2), button_size // 2)
       pygame.draw.rect(self.window, (255, 255, 255), button_rect.inflate(-button_size // 8, -button_size // 8))

       btn_start = self.font.render(f"{self.btn_start_stop}", True, (0, 0, 0))
        # 缩小start 文字字号 以适应按钮
       btn_start = pygame.transform.scale(btn_start, (button_size // 3 * 2, button_size // 3 * 2))
       self.window.blit(btn_start, (button_x + button_size // 2 - btn_start.get_width() // 2, button_y + button_size // 2 - btn_start.get_height() // 2))

       # 绘制滚轮动画
       if self.show_wheel:
           wheel_size = min(self.window_size) // 2
           wheel_x = self.window_size[0] // 2 - wheel_size // 2
           wheel_y = self.window_size[1] // 2 - wheel_size // 2
           wheel_rect = pygame.Rect(wheel_x, wheel_y, wheel_size, wheel_size)
           pygame.draw.circle(self.window, (0, 0, 0), (wheel_x + wheel_size // 2, wheel_y + wheel_size // 2), wheel_size // 2)
           pygame.draw.circle(self.window, (255, 255, 255), (wheel_x + wheel_size // 2, wheel_y + wheel_size // 2), wheel_size // 2 - 10)
           pygame.draw.line(self.window, (0, 0, 0), (wheel_x + 10, wheel_y + wheel_size // 2), (wheel_x + wheel_size - 10, wheel_y + wheel_size // 2))
           pygame.draw.line(self.window, (0, 0, 0), (wheel_x + wheel_size // 2, wheel_y + 10), (wheel_x + wheel_size // 2, wheel_y + wheel_size - 10))

           # 随机选择并显示菜谱
           menu_index = random.randint(0, len(self.menu) - 1)
           menu_surface = self.font.render(self.menu[menu_index], True, (0, 0, 0))
           self.window.blit(menu_surface, (wheel_x + wheel_size // 2 - menu_surface.get_width() // 2, wheel_y + wheel_size // 2 - menu_surface.get_height() // 2))
           self.cur_menu = self.menu[menu_index]
           # 播放一定帧数后停止动画
           self.wheel_count += 1
           if self.wheel_count > 500:
               self.show_wheel = False
               self.today_menu = self.cur_menu  # 自动停止赋值
       pygame.display.update()

if __name__ == "__main__":
   app = App()
作者:小松漫步
来源:juejin.cn/post/7272257829770887223


收起阅读 »

如何去除图片马赛克?

本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究! 一、前言 对图像不了解的人时常妄想去除马赛克是可以实现的,严格意义来说这确实是无法实现的。而深度学习是出现,让去除马赛克成为可能。 为了理解去除马赛克有多难,我们需要...
继续阅读 »

本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!



一、前言


对图像不了解的人时常妄想去除马赛克是可以实现的,严格意义来说这确实是无法实现的。而深度学习是出现,让去除马赛克成为可能。


为了理解去除马赛克有多难,我们需要知道马赛克是什么。观感上,马赛克就是方块感。当我们观察图像像素时, 马赛克表现为下图的情况:



原图右下角有十字,而添加马赛克后右下角一片都变成了同一像素,如果我们没保留原图,那么我们无法还原,也不知道是否还原了原图。因为原图已经被破坏了,这也是为什么马赛克是不可修复的。


那神经网络又是如何让修复成为可能呢?其实无论什么方式的修复,都是一种估计,而不是真正的修复。神经网络去除马赛克的操作其实是生成马赛克那部分内容,然后替代马赛克,从而达到修复的效果。


这种修复并不是还原,而是想象。假如我们对一张人脸打了马赛克,神经网络可以去除马赛克,但是去除后的人脸不再是原来那个人了。


二、实现原理


2.1 自编码器


图像修复的方法有很多,比如自编码器。自编码器是一种自监督模型,结构简单,不需要人为打标,收敛迅速。其结构如图:



编码器部分就是用于下采样的卷积网络,编码器会把图片编码成一个向量,而解码器则利用转置卷积把编码向量上采样成和原图大小一致的图片,最后我们把原图和生成结果的MSE作为损失函数进行优化。当模型训练好后,就可以用编码器对图片进行编码。


2.2 自编码器去除马赛克


那自编码器和去除马赛克有什么联系呢?其实非常简单,就是原本我们是输入原图,期望解码器能输出原图。这是出于我们希望模型学习如何编码图片的原图。而现在我们想要模型去除马赛克,此时我们要做的就是把马赛克图片作为输入,而原图作为输出,这样来训练就可以达到去除马赛克的效果了:



关于关于这种实现可以参考:juejin.cn/post/721068…


2.3 自编码器的问题


自编码器有个很明显的问题,就是图片经过编码器后会损失信息,而解码器的结果自然也会存在一些问题。这样既达不到去除马赛克的功能,连还原的原图都有一些模糊。


这里可以利用FPN的思想来改进,当自编码器加入FPN后,就得到了UNet网络结构。


2.4 UNet网络


UNet结构和自编码器类似,是一个先下再上的结构。和自编码器不同的时,UNet会利用编码器的每个输出,将各个输出与解码器的输入进行concatenate,这样就能更好地保留原图信息。其结构如下图:



UNet原本是用于图像分割的网络,这里我们用它来去除马赛克。


在UNet中,有几个部分我们分别来看看。


2.4.1 ConvBlock


在UNet中,有大量连续卷积的操作,这里我们作为一个Block(蓝色箭头),它可以实现为一个层,用PyTorch实现如下:


class ConvBlock(nn.Module):
def __init__(self, in_channels, out_channels):
super().__init__()
self.model = nn.Sequential(
nn.Conv2d(in_channels, out_channels, 3, 1, 1),
nn.BatchNorm2d(out_channels),
nn.ReLU(),
nn.Conv2d(out_channels, out_channels, 3, 1, 1),
nn.BatchNorm2d(out_channels),
nn.ReLU()
)

def forward(self, inputs):
return self.model(inputs)

这里其实就是两次卷积操作,这里的目的是提取当前感受野的特征。


2.4.2 ConvDown


经过连续卷积后,会使用卷积网络对图片进行下采样,这里把stride设置为2即可让图片缩小为原来的1/2。我们同样可以实现为层:


class ConvDown(nn.Module):
def __init__(self, channels):
super().__init__()
self.model = nn.Sequential(
nn.Conv2d(channels, channels, 3, 2, 1),
nn.BatchNorm2d(channels),
nn.ReLU()
)

def forward(self, inputs):
return self.model(inputs)

这里只有一个卷积,而且stride被设置为了2。


2.4.3 ConvUp


接下来是解码器部分,这里多了一个上采用的操作,我们可以用转置卷积完成,代码如下:


class ConvUp(nn.Module):
def __init__(self, channels):
super().__init__()
self.model = nn.Sequential(
nn.ConvTranspose2d(channels, channels // 2, 2, 2),
nn.BatchNorm2d(channels // 2),
nn.ReLU()
)

def forward(self, inputs):
return self.model(inputs)

上面是层可以把图片尺寸扩大为2倍,同时把特征图数量缩小到1/2。这里缩小特征图的操作是为了concatenate操作,后面详细说。


三、完整实现


首先,导入需要用的模块:


import os
import random
import torch
from torch import nn
from torch import optim
from torch.utils import data
import matplotlib.pyplot as plt
from torchvision import transforms
from torchvision.transforms import ToTensor
from PIL import Image, ImageDraw, ImageFilter
from torchvision.utils import make_grid

下面开始具体实现。


3.1 创建Dataset


首先创建本次任务需要的数据集,分布大致相同的图片即可,代码如下:


class ReConstructionDataset(data.Dataset):
def __init__(self, data_dir=r"G:/datasets/lbxx", image_size=64):
self.image_size = image_size
# 图像预处理
self.trans = transforms.Compose([
transforms.Resize(image_size),
transforms.CenterCrop(image_size),
transforms.ToTensor(),
# transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
# 保持所有图片的路径
self.image_paths = []
# 读取根目录,把所有图片路径放入image_paths
for root, dirs, files in os.walk(data_dir):
for file in files:
self.image_paths.append(os.path.join(root, file))

def __getitem__(self, item):
# 读取图片,并预处理
image = Image.open(self.image_paths[item])
return self.trans(self.create_blur(image)), self.trans(image)

def __len__(self):
return len(self.image_paths)


@staticmethod
def create_blur(image, return_mask=False, box_size=200):
mask = Image.new('L', image.size, 255)
draw = ImageDraw.Draw(mask)
upper_left_corner = (random.randint(0, image.size[0] - box_size), random.randint(0, image.size[1] - box_size))
lower_right_corner = (upper_left_corner[0] + box_size, upper_left_corner[1] + box_size)
draw.rectangle([lower_right_corner, upper_left_corner], fill=0)
masked_image = Image.composite(image, image.filter(ImageFilter.GaussianBlur(15)), mask)
if return_mask:
return masked_image, mask
else:
return masked_image

Dataset的实现与以往基本一致,实现init、getitem、len方法,这里我们还实现了一个create_blur方法,该方法用于生成矩形马赛克(实际上是高斯模糊)。下面是create_blur方法生成的图片:



3.2 网络构建


这里我们需要使用前面的几个子单元,先实现编码器,代码如下:


class UNetEncoder(nn.Module):
def __init__(self):
super().__init__()
self.blk0 = ConvBlock(3, 64)
self.down0 = ConvDown(64)
self.blk1 = ConvBlock(64, 128)
self.down1 = ConvDown(128)
self.blk2 = ConvBlock(128, 256)
self.down2 = ConvDown(256)
self.blk3 = ConvBlock(256, 512)
self.down3 = ConvDown(512)
self.blk4 = ConvBlock(512, 1024)

def forward(self, inputs):
f0 = self.blk0(inputs)
d0 = self.down0(f0)
f1 = self.blk1(d0)
d1 = self.down1(f1)
f2 = self.blk2(d1)
d2 = self.down2(f2)
f3 = self.blk3(d2)
d3 = self.down3(f3)
f4 = self.blk4(d3)
return f0, f1, f2, f3, f4

这里就是ConvBlok和ConvDown的n次组合,最终会得到一个1024×4×4的特征图。在forward中,我们返回了5个ConvBlok返回的结果,因为在解码器中我们需要全部使用。


接下来是解码器部分,这里与编码器相反,代码如下:


class UNetDecoder(nn.Module):
def __init__(self):
super().__init__()
self.up3 = ConvUp(1024)
self.blk3 = ConvBlock(1024, 512)
self.up2 = ConvUp(512)
self.blk2 = ConvBlock(512, 256)
self.up1 = ConvUp(256)
self.blk1 = ConvBlock(256, 128)
self.up0 = ConvUp(128)
self.blk0 = ConvBlock(128, 64)
self.last_conv = nn.Conv2d(64, 3, 3, 1, 1)

def forward(self, inputs):
f0, f1, f2, f3, f4 = inputs
u3 = self.up3(f4)
df2 = self.blk3(torch.concat((f3, u3), dim=1))
u2 = self.up2(df2)
df1 = self.blk2(torch.concat((f2, u2), dim=1))
u1 = self.up1(df1)
df0 = self.blk1(torch.concat((f1, u1), dim=1))
u0 = self.up0(df0)
f = self.blk0(torch.concat((f0, u0), dim=1))
return torch.tanh(self.last_conv(f))

解码器的inputs为编码器的5组特征图,在forward时需要与上采样结果concatenate。


最后,整个网络组合起来,代码如下:


class ReConstructionNetwork(nn.Module):

def __init__(self):
super().__init__()
self.encoder = UNetEncoder()
self.decoder = UNetDecoder()

def forward(self, inputs):
fs = self.encoder(inputs)
return self.decoder(fs)

3.3 网络训练


现在各个部分都完成了,可以开始训练网络:


device = "cuda" if torch.cuda.is_available() else "cpu"


def train(model, dataloader, optimizer, criterion, epochs):
model = model.to(device)
for epoch in range(epochs):
for iter, (masked_images, images) in enumerate(dataloader):
masked_images, images = masked_images.to(device), images.to(device)
outputs = model(masked_images)
loss = criterion(outputs, images)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (iter + 1) % 100 == 1:
print("epoch: %s, iter: %s, loss: %s" % (epoch + 1, iter + 1, loss.item()))
with torch.no_grad():
outputs = make_grid(outputs)
img = outputs.cpu().numpy().transpose(1, 2, 0)
plt.imshow(img)
plt.show()
torch.save(model.state_dict(), '../outputs/reconstruction.pth')


if __name__ == '__main__':
dataloader = data.DataLoader(ReConstructionDataset(r"G:\datasets\lbxx"), 64)
unet = ReConstructionNetwork()
optimizer = optim.Adam(auto_encoder.parameters(), lr=0.0002)
criterion = nn.MSELoss()
train(unet, dataloader, optimizer, criterion, 20)

训练完成后,就可以用来去除马赛克了,代码如下:



dataloader = data.DataLoader(ReConstructionDataset(r"G:\datasets\lbxx"), 64, shuffle=True)
unet = ReConstructionNetwork().to(device)
unet.load_state_dict(torch.load('../outputs/reconstruction.pth'))
for masked_images, images in dataloader:
masked_images, images = masked_images.to(device), images.to(device)
with torch.no_grad():
outputs = unet(masked_images)
outputs = torch.concatenate([images, masked_images, outputs], dim=-1)
outputs = make_grid(outputs)
img = outputs.cpu().numpy().transpose(1, 2, 0)
img = cv2.normalize(img, , 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
Image.fromarray(img).show()


下面是生成结果。左侧为原图,中间为添加马赛克后的图片,右侧则是去除马赛克后的结果:



整体来说效果比较不错。本文的方法不只可以用来去除马赛克,还可以完成图像重构。比如老化的图片、被墨汁污染的图片等,都可以用本文的方法完成重构。另外,本文的数据有限,实现效果并不通用,有需求的读者可以移步CodeFormer项目:github.com/sczhou/Code…


作者:ZackSock
来源:juejin.cn/post/7265310874488520765
收起阅读 »

python复数类型的使用及介绍

在Python中,复数类型是用来表示具有实部和虚部的数值。复数由实部和虚部组成,形式为 a + bj,其中 a 是实部,b 是虚部,j 是虚数单位。 要创建一个复数,可以使用 complex() 函数,并提供实部和虚部作为参数。例如: z =&n...
继续阅读 »

在Python中,复数类型是用来表示具有实部和虚部的数值。复数由实部和虚部组成,形式为 a + bj,其中 a 是实部,b 是虚部,j 是虚数单位。


要创建一个复数,可以使用 complex() 函数,并提供实部和虚部作为参数。例如:


z = complex(23)
print(z)  # 输出:(2+3j)

这里,z 是一个复数,实部为2,虚部为3。


可以通过 .real 属性来访问复数的实部,通过 .imag 属性来访问复数的虚部。例如:


print(z.real)  # 输出:2.0
print(z.imag)  # 输出:3.0

可以使用运算符来对复数进行算术运算,包括加法、减法、乘法和除法。例如:


z1 = complex(23)
z2 = complex(45)

addition = z1 + z2
subtraction = z1 - z2
multiplication = z1 * z2
division = z1 / z2

print(addition)      # 输出:(6+8j)
print(subtraction)   # 输出:(-2-2j)
print(multiplication) # 输出:(-7+22j)
print(division)      # 输出:(0.5609756097560976+0.0487804878048781j)

Python还提供了一些内置函数和方法来处理复数,例如 abs() 函数用于计算复数的绝对值,cmath 模块提供了一些数学函数,如求幅度和相位角。


复数的使用可以在需要处理虚数或使用复数运算的情况下非常有用,例如在工程、物理或数学领域。


作者:小小绘
来源:mdnice.com/writing/2cd491bb2ae749d195b965325ba408f3
收起阅读 »

Python中列表的惭怍方法

Python中的列表是一种非常常用的数据结构,它可以存储多个元素,并且可以进行各种操作。下面是关于列表操作的一些基本方法:列表的生成:使用方括号 [] 来创建一个空列表:my_list = []使用方括号 [] 并在其中添加元素来创建一个非空列表:my_lis...
继续阅读 »

Python中的列表是一种非常常用的数据结构,它可以存储多个元素,并且可以进行各种操作。下面是关于列表操作的一些基本方法:

  1. 列表的生成:

    • 使用方括号 [] 来创建一个空列表:my_list = []

    • 使用方括号 [] 并在其中添加元素来创建一个非空列表:my_list = [1, 2, 3]

    • 使用列表生成式来生成列表:my_list = [x for x in range(5)]

  2. 列表的增加和删除:

    • 使用 append() 方法在列表末尾添加一个元素:my_list.append(4)

    • 使用 insert() 方法在指定位置插入一个元素:my_list.insert(0, 0)

    • 使用 extend() 方法将另一个列表的元素添加到当前列表末尾:my_list.extend([5, 6, 7])

    • 使用 remove() 方法删除列表中的指定元素:my_list.remove(3)

    • 使用 pop() 方法删除并返回列表中指定位置的元素:my_list.pop(0)

  3. 列表的遍历和循环:

    • 使用 for 循环遍历列表中的每个元素:

      for item in my_list:
          print(item)
    • 使用 enumerate() 函数同时获取元素的索引和值:

      for index, item in enumerate(my_list):
        print(index, item)
    • 使用 while 循环根据条件遍历列表:

      i = 0
      while i < len(my_list):
        print(my_list[i])
        i += 1
    • 使用 range() 函数和 len() 函数结合来遍历列表的索引:

        for i in range(len(my_list)):
            print(my_list[i])

希望这些例子能帮助你更好地理解列表的操作方法。如果有任何问题,请随时提问。

作者:orangewu
来源:mdnice.com/writing/3a3a6e2f2a5c4763a2c8901e205f446c
收起阅读 »

😋贪心算法

贪心算法 贪心算法是一种寻找最优解的算法思想,它通过局部最优选择来达到全局最优解。在贪心算法中,每一步都会做出当前状态下的最优选择,并且假设做出这样的选择后,剩余的问题可以被简化为一个更小的子问题。 与动态规划不同,贪心算法不需要保存子问题的解,因此通常需要更...
继续阅读 »

贪心算法


贪心算法是一种寻找最优解的算法思想,它通过局部最优选择来达到全局最优解。在贪心算法中,每一步都会做出当前状态下的最优选择,并且假设做出这样的选择后,剩余的问题可以被简化为一个更小的子问题。


与动态规划不同,贪心算法不需要保存子问题的解,因此通常需要更少的空间和时间。


贪心算法通常采用一种贪心的策略,即在每一步选择当前看起来最优的选择,希望最终得到全局最优解。但是,在某些情况下,局部最优解并不能保证一定能够导致全局最优解。由于贪心算法一旦做出选择就不能更改。贪心算法只是一种近似算法。


贪心算法通常需要满足贪心选择性质和最优子结构性质,否则它可能会导致错误的结果。


在使用贪心算法时,我们需要仔细考虑问题的特点和贪心选择的合理性,并尽可能地证明贪心算法的正确性。如果无法证明贪心算法的正确性,我们需要考虑使用其他算法来解决问题。


贪心算法常见的应用场景包括:



  • 贪心选择性质:在求解最优解的过程中,每一步的选择只与当前状态有关,不受之前选择的影响。

  • 最优子结构性质:问题的最优解可以被分解为若干个子问题的最优解,即子问题的最优解可以推导出原问题的最优解。

  • 无后效性:某个状态以前的过程不会影响以后的状态,只与当前状态有关。


举个反例🌰:279. 完全平方数


给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。


完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,149 和 16 都是完全平方数,而 3 和 11 不是。


 


示例 1:


输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4

示例 2:


输入: n = 13
输出: 2
解释: 13 = 4 + 9

 


提示:



  • 1<=n<=1041 <= n <= 10^4


错误做法:


class Solution:
def numSquares(self, n: int) -> int:
count = 0
while n != 0:
c = int(n**(1/2))
n -= c**2
count += 1
return count

输入12的时候答案是4,也就是12 = 9 + 1 + 1 + 1


实际上应该是答案为312 = 4 + 4 + 4


这个函数使用的是贪心算法的思想,每次都选择当前能用的最大完全平方数来减去 n,直到 n 减为 0。


在每一步中,选择最大的完全平方数来减去 n,可以确保所需的完全平方数的数量最小,因为如果我们选择了小的完全平方数,那么我们需要更多的完全平方数才能表示 n。


但是它并没有证明贪心策略的正确性,也没有提供正确性的证明。我们已经提供反例,证明这玩意儿是错的了。贪心算法的正确性得不到保证,所以本题不能用贪心算法。


正确答案:


class Solution:
def numSquares(self, n: int) -> int:
dp = [float('inf')]*(n+1)
dp[0] = 0
for i in range(1,n+1):
j = 1
while j*j <= i:
dp[i] = min(dp[i],dp[i-j*j]+1)
j+=1
return dp[-1]

这个代码使用了动态规划来解决完全平方数问题,它的时间复杂度为 O(nn)O(n\sqrt{n}),空间复杂度为 O(n)O(n)




  • i=0 时,不需要任何完全平方数。




  • 对于 i>0 的情况,我们枚举从 1i 中的每个完全平方数 j*j,然后计算 dp[i-j*j]+1 的值,这个值表示在将 i-j*j 分解成完全平方数之和的基础上再加上一个完全平方数 j*j。我们需要使 dp[i-j*j]+1 的值最小,因此我们可以得出状态转移方程:




dp[i]=min(dp[i],dp[ijj]+1)dp[i] = min(dp[i], dp[i-j * j]+1)

最后,dp[n] 的值就是将 n 分解成完全平方数之和所需的最小个数。


该代码正确地解决了完全平方数问题,可以得到全局最优解。


55. 跳跃游戏


给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。


数组中的每个元素代表你在该位置可以跳跃的最大长度。


判断你是否能够到达最后一个下标。


 


示例 1:


输入: nums = [2,3,1,1,4]
输出: true
解释: 可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:


输入: nums = [3,2,1,0,4]
输出: false
解释: 无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

 


提示:



  • 1 <= nums.length <= 3 * 104

  • 0 <= nums[i] <= 105


class Solution:
def canJump(self, nums: List[int]) -> bool:
maxlen = 0
for i,n in enumerate(nums):
if maxlen < i:
return False
maxlen = max(maxlen,i+n)
return maxlen >= len(nums) -1

这段代码实现了一个非常经典的贪心算法,用于判断能否从数组的起点跳到终点。


具体思路是,用 maxlen 记录当前能到达的最远位置,遍历数组中的每个位置,如果当前位置大于 maxlen,说明无法到达该位置,直接返回 False。否则,更新 maxlen 为当前位置能够到达的最远位置。


这个算法的贪心策略是,在每个位置上都选择能够到达的最远位置。由于跳跃的步数只能是整数,所以如果当前位置能到达的最远位置小于当前位置,那么就无法到达该位置。


这个算法的时间复杂度是 O(n)O(n),空间复杂度是 O(1)O(1)


45. 跳跃游戏 II


给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]


每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:



  • 0 <= j <= nums[i] 

  • i + j < n


返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]


 


示例 1:


输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
  从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

示例 2:


输入: nums = [2,3,0,1,4]
输出: 2

 


提示:



  • 1 <= nums.length <= 104

  • 0 <= nums[i] <= 1000

  • 题目保证可以到达 nums[n-1]


class Solution:
def jump(self, nums) -> int:
minstep = 0
i = len(nums) - 1
while i > 0:
for j,n in enumerate(nums):
if j+n >= i:
minstep += 1
i = j
break
return minstep

该算法的时间复杂度为 O(n2)O(n^2),其中 nn 为数组的长度。


在最坏情况下,每个元素都需要遍历一遍,以找到它们能够到达的最远距离,这需要 O(n)O(n) 的时间复杂度。同时,每次找到能够到达 ii 的最远距离时,都需要遍历从 00i1i-1 的所有元素,以找到能够到达 ii 的最小步数,这也需要 O(n)O(n) 的时间复杂度。因此,总时间复杂度为 O(n2)O(n^2)


该算法的空间复杂度为 O(1)O(1),因为它只使用了常数级别的额外空间。


优化——从前往后跳:


这个算法是一个基于贪心策略的解法,跟之前的从前往后跳的贪心算法类似,不过稍微做了一些改进,可以将时间复杂度降低到 O(n)O(n)


算法的核心思想是维护一个区间 [0, end],在这个区间内每个位置所能跳到的最远距离都是 i + nums[i],其中 i 是当前位置,nums[i] 是当前位置所能跳的最远距离。维护的时候,我们不断更新能够到达的最远距离 maxlen,当 i 到达区间的末尾 end 时,说明需要跳一步,并将 end 更新为 maxlen


这个算法的时间复杂度为 O(n)O(n),空间复杂度为 O(1)O(1)


class Solution:
def jump(self, nums):
n = len(nums)
maxlen = end = 0
step = 0
for i in range(n - 1):
maxlen = max(maxlen, i + nums[i])
if i == end:
end = maxlen
step += 1
return step
作者:Ann
来源:juejin.cn/post/7262231954191859770

收起阅读 »

python计算质数的几种方法

因为要学着写渗透工具,这几天都在上python编程基础课,听得我打瞌睡,毕竟以前学过嘛。 最后sherry老师留了作业,其中一道题是这样的: 题目:编写python程序找出10-30之间的质数。 太简单了,我直接给出答案: Prime = [11, 13, 1...
继续阅读 »

因为要学着写渗透工具,这几天都在上python编程基础课,听得我打瞌睡,毕竟以前学过嘛。
最后sherry老师留了作业,其中一道题是这样的:


题目:编写python程序找出10-30之间的质数。


太简单了,我直接给出答案:


Prime = [11, 13, 17, 19, 23, 29]
print(Prime)

输出结果:


[11, 13, 17, 19, 23, 29]

当然,这样做肯定会在下节课被sherry老师公开处刑的,所以说还是要根据上课时学的知识写个算法。


1.穷举法


回想一下上课时学了变量、列表、循环语句之类的东西,sherry老师还亲自演示了多重死循环是怎么搞出来的(老师是手滑了还是业务不熟练啊),所以我们还是要仔细思考一下不要重蹈覆辙。


思路:首先要构造一个循环,遍历所有符合条件的自然数,然后一个一个验证是否为质数,最后把符合条件的质数列出来。


# 最开始编的穷举法,简单粗暴,就是性能拉跨。
# P=因数,N=自然数
import time

t0 = time.time() # 开始时间
Min = 10 # 范围最小值
Max = 30 # 范围最大值
Result = [] # 结果

for N in range(Min, Max): # 给自然数来个遍历
for P in range(2, N):
if (N % P == 0): # 判断是否有因数
break # 有因数那就不是质数,跳出循环
else:
Result.append(N)

print('计算', Min, '到', Max, '之间的质数')
print(Min, '到', Max, '之间的质数序列:', Result)
print(Min, '到', Max, '之间的质数个数:', len(Result))
print('计算耗时:', time.time() - t0, '秒')

执行结果(计算耗时是最后加上去的):


2023-05-28-22-35-46.png


到这里作业就搞定了。然后把其他几道题也做完了,发现很无聊,就又切回来想搞点事。这么点计算量,0秒真的有点少,不如趁这个机会烤一烤笔记本的性能,所以直接在Min和Max的值后面加几个0。试试100000-200000。


2023-05-28-23-02-03.png


很尴尬,直接卡住了,这代码有点拉跨啊,完全不符合我的风格。
倒了杯咖啡,终于跑完了。


2023-05-28-23-01-07.png


这个也太夸张,一定是哪里出了问题,很久以前用C写的代码我记得也没那么慢啊。反正周末挺闲的,不如仔细研究一下。


2.函数(CV)大法


为了拓宽一下思路,我决定借鉴一下大佬的代码。听说函数是个好东西,所以就CV了两个函数。


一个函数判断质数,另一个求范围内的所有质数,把它们拼一起,是这个样子:


# 网上学来的,自定义两个函数,但是数值稍微大点就卡死了。
import time

t0 = time.time()
Min = 100000 # 范围最小值
Max = 200000 # 范围最大值


def is_prime(n): return 0 not in [n % i for i in range(2, n//2+1)] # 判断是否为质数


def gen_prime(a, b): return [n for n in range(
a, b+1) if 0 not in [n % i for i in range(2, n//2+1)]] # 输出范围内的质数


print('计算', Min, '到', Max, '之间的质数')
print(Min, '到', Max, '之间的质数序列:', gen_prime(Min, Max))
print('计算耗时:', time.time() - t0, '秒')

稍微改动了一下,还是100000-200000,我们试试看。


2023-05-28-23-08-35.png


嗯,一运行风扇就开始啸叫,CPU都快烤炸了。看来CV大法也不行啊。
经过漫长的烤机,这次结果比上次还惨,300多秒,这两个函数本质上还是穷举法,看来这条路也走不通。


3.穷举法改


我们可以分析一下穷举法的代码,看看有没有什么改进的方法。
首先,通过九年义务教育掌握的数学知识,我们知道,质数中只有2是偶数,所以计算中可以把偶数忽略掉,只计算奇数,工作量立马减半!
其次,在用因数P判断N是否为质数时,如果P足够大的话,比如说PxP>=N的时候,那么后面的循环其实是重复无意义的。因为假设PxQ>=N,那么P和Q必然有一个小于sqrt(N),只需要计算P<=sqrt(N)的情况就行了。


因为2作为唯一一个偶数,夹在循环里面处理起来很麻烦,所以放在开头处理掉。最终的代码如下:


# 优化后的代码,减少了一些无意义的循环,比以前快多了。
import time

t0 = time.time()
Min = 100000 # 范围最小值
Max = 200000 # 范围最大值
Prime = [2, 3] # 质数列表
Result = [] # 结果
Loop = 0 # 计算循环次数

if Min <= 2:
Result.append(2)
if Min <= 3:
Result.append(3) # 先把2这个麻烦的偶数处理掉
for N in range(5, Max, 2):
for P in range(3, int(N**0.5)+2, 2): # 只计算到根号N
Loop += 1
if (N % P == 0):
break
else:
Prime.append(N)
if N > Min:
Result.append(N)

print('计算', Min, '到', Max, '之间的质数')
print(Min, '到', Max, '之间的质数序列:', Result)
print(Min, '到', Max, '之间的质数个数:', len(Result))
print('循环次数:', Loop)
print('计算耗时:', time.time() - t0, '秒')

2023-05-28-23-09-54.png


代码量虽然多了,但是效果还是很明显,100000-200000才0.4秒,快了不知道多少,看来我们的思路是对的。
我决定再加到1000000-5000000,看看能不能撑住。因为输出太多了控制台会卡死,所以改一下,只输出最后一个质数。


2023-05-28-23-19-12.png


总共花了64秒,看来还是有点费劲。


4.穷举法魔改


我们再来分析一下,如果我们用于判断的因数,不是用奇数列表,而是用生成的Prime列表里面的质数,因为质数的个数远远少于奇数,所以第二个循环会少一些工作量呢?可以试试看。但是因为这个改动,需要加一些判断语句进去,所以节省的时间比较有限。


# 别看这个代码比较长,但是跑到1000万也不会卡死,而且还很快。
import time

t0 = time.time()
Min = 1000000 # 范围最小值
Max = 5000000 # 范围最大值
Prime = [2, 3] # 质数列表
Result = [] # 结果
Loop = 0 # 计算循环次数

if Min <= 2:
Result.append(2)
if Min <= 3:
Result.append(3)
for N in range(5, Max, 2):
M = int(N**0.5) # 上限为根号N
for P in range(len(Prime)): # 在质数列表Prime中遍历
Loop += 1
L = Prime[P+1]
if (N % L == 0):
break
elif L >= M: # 上限大于根号N,判断为质数并跳出循环
Prime.append(N)
if N > Min:
Result.append(N)
break

print('计算', Min, '到', Max, '之间的质数')
print('最后一个质数:', Result[-1])
print(Min, '到', Max, '之间的质数个数:', len(Result))
print('循环次数:', Loop)
print('计算耗时:', time.time() - t0, '秒')

还是1000000-5000000再试试看


2023-05-28-23-25-29.png


这次耗时22秒,时间又缩短了一大半,但是好像已经没多少改进的空间了,感觉穷举法已经到头了,需要新的思路。


5.埃氏筛法


其实初中数学我们就学过埃氏筛法:
如果P是质数,那么大于P的N的倍数一定不是质数。把所有的合数排除掉,那么剩下的就都是质数了。
我们可以生成一个列表用来储存数字是否是质数,初始阶段都是质数,每次得出一个质数就将它的倍数全部标记为合数。


# 速度已经起飞了。
import time

t0 = time.time()
Min = 1000000 # 范围最小值
Max = 2000000 # 范围最大值
Loop = 0 # 计算循环次数
Result = [] # 结果

Natural = [True for P in range(Max)] # 自然数列表标记为True
for P in range(2, Max):
if Natural[P]: # 标记如果为True,就是质数
if P >= Min:
Result.append(P) # 添加范围之内的质数
for N in range(P*2, Max, P): # 将质数的倍数的标记改为False
Loop += 1
Natural[N] = False

print('计算', Min, '到', Max, '之间的质数')
print('最后一个质数:', Result[-1])
print(Min, '到', Max, '之间的质数个数:', len(Result))
print('循环次数:', Loop)
print('计算耗时:', time.time() - t0, '秒')

2023-05-29-00-11-23.png


1.6秒,比最高级的穷举法还要快上10多倍,这是数学的胜利。
再试试1-50000000。


1.png


很不错,只需要20秒。因为筛法的特性,忽略内存的影响,数值越大,后面的速度反而越快了。


6.欧拉筛法


我们可以仔细分析一下,上面的埃氏筛法在最后标记的时候,还是多算了一些东西,N会重复标记False,比如77,既是7的倍数又是11的倍数,这样会被标记两次,后面的大合数会重复标记多次,浪费了算力,所以标记的时候要排除合数。另外就是P*N大于Max时,后面的计算已经无意义了,也要跳出来。把这些重复的动作排除掉,就是欧拉筛法,也叫线性筛。


# 最终版,优化了很多细节。
import time

t0 = time.time()
Min = 1 # 范围最小值
Max = 50000000 # 范围最大值
Loop = 0 # 计算循环次数
Prime = [2]
Result = [] # 结果

if Min <= 2:
Result.append(2)
Limit = int(Max/3)+1
Natural = [True for P in range(Max+1)] # 自然数列表标记为True
for P in range(3, Max+1, 2):
if Natural[P]: # 标记如果为True,就是质数
Prime.append(P)
if P >= Min:
Result.append(P)
if P > Limit: # 超过Limit不需要再筛了,直接continue
continue
for N in Prime: # 将质数的倍数的标记改为False
Loop += 1
if P*N > Max: # 超过Max就无意义了,直接break
break
Natural[P * N] = False
if P % N == 0: # 判断是否为合数
break

print('计算', Min, '到', Max, '之间的质数')
print('最后一个质数:', Result[-1])
print(Min, '到', Max, '之间的质数个数:', len(Result))
print('循环次数:', Loop)
print('计算耗时:', time.time() - t0, '秒')

(因为之前的版本缩进错了,所以更新了这段代码)


2.png


同样的条件下耗时11.46秒。这是因为多了一个列表和几行判断语句,加上python的解释型特性,所以实际上并不会快好几倍,但是总体效率还是有50%左右的提升。


好了,这次把老师课堂上讲的变量、列表、循环语句什么的都用上了,算是现买现卖、活学活用吧。我觉得这次的作业怎么说也能拿满分吧,sherry老师记得下次上课夸夸我。


作者:ReisenSS
来源:juejin.cn/post/7238199999732695097
收起阅读 »

微信图片防撤回

了解需求 实际生活中,由于好奇朋友撤回的微信图片信息,但直接去要又怎会是我的性格呢。由此萌生出做一个微信防撤回程序(已向朋友说明)。 当前网络上其实存在一些微信防撤回程序,不过担心不正规软件存在漏洞,泄漏个人信息,这里也就不考虑此种方法。 解决方案 思路 由于...
继续阅读 »

了解需求


实际生活中,由于好奇朋友撤回的微信图片信息,但直接去要又怎会是我的性格呢。由此萌生出做一个微信防撤回程序(已向朋友说明)。


当前网络上其实存在一些微信防撤回程序,不过担心不正规软件存在漏洞,泄漏个人信息,这里也就不考虑此种方法。


解决方案


思路


由于当前微信不支持微信网页版登陆,因此使用itchat的方法不再适用。


后来了解到电脑端微信图片会先存储在本地,撤回后图片再从本地删除,因此只要在撤回前将微信本地图片转移到新文件夹即可。


在此使用Python的watchdog包来监视文件系统事件,例如文件被创建、修改、删除、移动,我们只需监听创建文件事件即可。


安装watchdog包:    pip install watchdog
我的python环境为python3.9版本

实现


1.首先进行文件创建事件监听,在监听事件发生后的事件处理对象为复制微信图片到新文件夹。具体代码如下。


需要注意的是微信在2022.05前,图片存储在images目录下;在2022.05后,图片存储在MsgAttach目录下,并按微信对象分别进行存储。


# 第一步:加载路径,并实时读取JPG信息
import os
import shutil
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

def mycopyfile(srcfile,dst_dir):
if not os.path.isfile(srcfile):
print ("%s not exist!"%(srcfile))
else:
fpath,fname=os.path.split(srcfile) # 分离文件名和路径
if fname.endswith('.jpg') or fname.endswith('.png') or fname.endswith('.dat'):
dst_path = os.path.join(dst_dir, fname)
shutil.copy(srcfile, dst_path) # 复制文件

class MyEventHandler(FileSystemEventHandler):
# 文件移动
# def on_moved(self, event):
# print("文件移动触发")
# print(event)


def on_created(self, event):
# print("文件创建触发")
print(event)
mycopyfile(event.src_path, dst_dir)


# def on_deleted(self, event):
# print("文件删除触发")
# print(event)
#
# def on_modified(self, event):
# print("文件编辑触发")
# print(event)

if __name__ == '__main__':

dst_dir = r"E:\03微信防撤回\weixin" #TODO:修改为自己的保存文件目录
if not os.path.exists(dst_dir):
os.makedirs(dst_dir)

observer = Observer() # 创建观察者对象
file_handler = MyEventHandler() # 创建事件处理对象
listen_dir = r"C:\Users\hc\Documents\WeChat" #TODO:修改为自己的监听目录
observer.schedule(file_handler, listen_dir, True) # 向观察者对象绑定事件和目录
observer.start() # 启动
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()

2.由于微信保存文件以.dat格式保存,因此需要对微信文件格式进行解码,具体解码代码如下。


# weixin_Image.dat 破解
# JPG 16进制 FF D8 FF
# PNG 16进制 89 50 4e 47
# GIF 16进制 47 49 46 38
# 微信.bat 16进制 a1 86----->jpg ab 8c----jpg dd 04 --->png
# 自动计算异或 值
import os

into_path = r'E:\03微信防撤回\weixin' # 微信image文件路径
out_path = r'E:\03微信防撤回\image'

def main(into_path, out_path):

dat_list = Dat_files(into_path) # 把路径文件夹下的dat文件以列表呈现
lens = len(dat_list)
if lens == 0:
print('没有dat文件')
exit()

num = 0
for dat_file in dat_list: # 逐步读取文件
num += 1
temp_path = into_path + '/' + dat_file # 拼接路径:微信图片路径+图片名
dat_file_name = dat_file[:-4] # 截取字符串 去掉.dat
imageDecode(temp_path, dat_file_name, out_path) # 转码函数
value = int((num / lens) * 100) # 显示进度
print('正在处理--->{}%'.format(value))


def Dat_files(file_dir):
"""
:param file_dir: 寻找文件夹下的dat文件
:return: 返回文件夹下dat文件的列表
"""

dat = []
for files in os.listdir(file_dir):
if os.path.splitext(files)[1] == '.dat':
dat.append(files)
return dat

def imageDecode(temp_path, dat_file_name, out_path):
dat_read = open(temp_path, "rb") # 读取.bat 文件
xo, j = Format(temp_path) # 判断图片格式 并计算返回异或值 函数

if j == 1:
mat = '.png'
elif j == 2:
mat = '.gif'
else:
mat = '.jpg'

out = out_path + '/' + dat_file_name + mat # 图片输出路径
png_write = open(out, "wb") # 图片写入
dat_read.seek(0) # 重置文件指针位置

for now in dat_read: # 循环字节
for nowByte in now:
newByte = nowByte ^ xo # 转码计算
png_write.write(bytes([newByte])) # 转码后重新写入


def Format(f):
"""
计算异或值
各图片头部信息
png:89 50 4e 47
gif: 47 49 46 38
jpeg:ff d8 ff
"""

dat_r = open(f, "rb")

try:
a = [(0x89, 0x50, 0x4e), (0x47, 0x49, 0x46), (0xff, 0xd8, 0xff)]
for now in dat_r:
j = 0
for xor in a:
j = j + 1 # 记录是第几个格式 1:png 2:gif 3:jpeg
i = 0
res = []
now2 = now[:3] # 取前三组判断
for nowByte in now2:
res.append(nowByte ^ xor[i])
i += 1
if res[0] == res[1] == res[2]:
return res[0], j
except:
pass
finally:
dat_r.close()


# 运行
if __name__ == '__main__':
main(into_path, out_path)
复制代码
作者:空气猫
来源:juejin.cn/post/7221376169370583101
>
收起阅读 »

python简单实现校园网自动认证,赶紧部署到你的路由器上吧

2023-05-20:使用python库来实现定时任务,不再依赖系统的定时任务,部署起来容易100倍 原文链接 python实现校园网自动认证 - 歌梦罗 - 努力生活,努力热爱 (gmero.com) 说在前面 大部分校园网都需要认证才能够上网,而且就...
继续阅读 »

2023-05-20:使用python库来实现定时任务,不再依赖系统的定时任务,部署起来容易100倍




原文链接 python实现校园网自动认证 - 歌梦罗 - 努力生活,努力热爱 (gmero.com)



说在前面


大部分校园网都需要认证才能够上网,而且就算你保持在线隔一段时间也会将你强行踢下线,导致游戏中断,自己挂在校园网的web程序宕机等头疼的问题。


本文主要是使用python与主机的计划任务来实现校园网的自动认证,需要注意的是,我们学校的校园网认证系统与认证方式可能与你们学校的不一样,所以主要是分享我的实现思路,以供大家参考参考。


准备工作:



  • 一台电脑

  • 一台用于挂载自动认证脚本的服务器(也可以是你的电脑,或者不那么硬的路由器之类的)


了解校园网认证的过程



这是最繁琐的一步,在网上见过的这么多认证的教程,感觉我们学校是最复杂的



其实总结来说这一步就是反复的在浏览器f12里观察你在认证的时候都经过了哪些程序,我们学校的认证流程大概是这样的



我们需要传入以下参数进行认证,其中后三项传空值,因为是本地认证后面脚本就懒得加密密码了,把passwordEncrypt传false,password直接明文就可以了,可能不安全,但校园网无所谓。



看起来感觉实现很容易对不对?python随便几行request就能实现了的。但有以下几个问题:



  • 重复的认证是无效的,不会重新计算在线时间(到点还是踢你)

  • 高频的认证可能导致莫名奇妙的bug,比如明明已经认证成功了而认证服务器那边确觉得你没有认证,导致认证界面直接卡死(因为123.123.123.123在你已经接入互联网的情况下是无法访问的)

  • 多次认证还可能导致出现验证码,目前脚本还无法处理验证码


于是我们在程序中需要判断是否已经认证了,在已经认证的情况下运行程序需要先登出来重置在线时间从而推迟强制下线的时间(于是,我们又需要找到两个请求:一个是判断认证情况的,一个是退出登录的)


经过疯狂的F12和api调试之后,找到了172.208.2.102/eportal/InterFace.do?method=logout172.208.2.102/eportal/InterFace.do?method=getUserInfo两个api来进行登出和判断操作


python实现


认证流程用python的requests库很容易就能实现,定时任务用schedule来创建也很容易。


import re
import time
import urllib.parse
from urllib.parse import urlparse

import requests
import schedule

apiUrl = "http://172.208.2.102/eportal/InterFace.do"
authUrl = apiUrl + "?method=login"
logoutUrl = apiUrl + "?method=logout"
getQueryUrl = "http://123.123.123.123/"
getUserInfoUrl = apiUrl + "?method=getOnlineUserInfo"
webService = "中国电信"


# 判断是否已经认证了
def is_authed():
   try:
       resp = requests.get(getUserInfoUrl, timeout=3)
       if resp.status_code != 200:
           # 网络问题直接退出
           print("判断认证状态失败")
           return False
       else:
           json_data = resp.json()
           result = json_data["result"]
           return result != "fail"
   except requests.RequestException:
       print("判断认证状态失败: 请求失败")
       return False


# 获取query认证信息
def get_query_str():
   try:
       resp = requests.get(getQueryUrl, timeout=8)
       if resp.status_code != 200:
           print("获取query信息失败")
           return False
       pattern = "href='(.*)'"
       match = re.search(pattern, resp.text)

       if match:
           url = urlparse(match.group(1))
           return url.query
       else:
           return
   except requests.RequestException:
       print("获取query信息失败: 请求失败")
       return False


# 认证
def do_auth():
   query_str = get_query_str()
   if not query_str:
       return False
   # 表单数据
   data = {
       "userId": "871390441",
       "password": "yourpassword",
       "service": urllib.parse.quote(webService),
       "queryString": query_str,
       "passwordEncrypt": "false",
       "operatorPwd": ,
       "operatorUserId": ,
       "validcode":
  }

   try:
       resp = requests.post(authUrl, data)
       if resp.status_code == 200 and resp.json()["result"] == "success":
           print("认证成功")
           return True
       else:
           print("认证失败")
           return False
   except requests.RequestException:
       print("认证失败: 请求失败")
       return False


# 退出登录
def do_logout():
   resp = requests.get(logoutUrl)

   if resp.status_code == 200:
       if resp.json()["result"] == "success":
           print("退出登录成功")
           return True
       else:
           print("退出登录失败: " + resp.json()["message"])
           return False
   else:
       print("退出登录失败: 网络错误")
       return False


# 一次认证流程
def auth_job():
   print("\n====校园网自动认证开始====")
   if is_authed():
       if do_logout():
           do_auth()
   else:
       do_auth()
   print("====校园网自动认证结束====\n")


if __name__ == '__main__':
   auth_job()
   # 定时任务
   schedule.every().day.at("12:00").do(auth_job)
   schedule.every().day.at("00:00").do(auth_job)

   while True:
       schedule.run_pending()
       time.sleep(1)


代码部分有了思路之后其实就很简单了,接下来就是最重要的部署环节了。


部署到服务器


我这里以部署到我的破烂x86linux服务器上为例(windows就更简单了,直接运行python程序即可),采取docker部署的方式


首先写Dockerfile, 这里我就不解释了,不懂的话找一找资料吧


FROM python:3.11-slim-bullseye

WORKDIR /app

ADD . /app

RUN pip3 config set global.index-url http://mirrors.aliyun.com/pypi/simple && \
  pip3 config set install.trusted-host mirrors.aliyun.com && \
  pip install --upgrade pip &&\
  pip3 install requests &&\
  pip3 install schedule


CMD python3 -u main.py

然后在当前文件夹输入命令来建立镜像和运行容器,很简单对不对


docker build -t autoauth:v1 . 
docker run --restart always --name myautoauth autoauth:v1

写在最后


以上操作在我这是完美运行的,需要一些折腾,但你能看完我这篇博客说明你肯定也是个喜欢折腾的人吧。上面的一些命名方法路径之类的不一定是绝对的。


作者:歌梦罗
来源:juejin.cn/post/7234864940799213625
收起阅读 »

快速生成定制化的Word文档:Python实践指南

1.1. 前言 众所周知,安服工程师又叫做Word工程师,在打工或者批量SRC的时候,如果产出很多,又需要一个一个的写报告的情况下会非常的折磨人,因此查了一些相关的资料,发现使用python的docxtpl库批量写报告效果很不错,记录一下。 1.2. 介绍 d...
继续阅读 »

1.1. 前言


众所周知,安服工程师又叫做Word工程师,在打工或者批量SRC的时候,如果产出很多,又需要一个一个的写报告的情况下会非常的折磨人,因此查了一些相关的资料,发现使用python的docxtpl库批量写报告效果很不错,记录一下。


1.2. 介绍


docxtpl 是一个用于生成 Microsoft Word 文档的模板引擎库,它结合了 docx 模块和 Jinja2 模板引擎,使用户能够使用 Microsoft Word 模板文件并在其中填充动态数据。它提供了一种方便的方式来生成个性化的 Word 文档,并支持条件语句、循环语句和变量等控制结构,以满足不同的文档生成需求。


官方GitHub地址:github.com/elapouya/py…


官方文档地址:docxtpl.readthedocs.io/en/latest/



简单来说:就是创建一个类似Jinja2语法的模板文档,然后往里面动态填充内容就可以了



安装:


pip3 install docxtpl

1.3. 基础使用


from docxtpl import DocxTemplate

doc = DocxTemplate("test.docx")
context = {'whoami': "d4m1ts"}
doc.render(context)
doc.save("generated_doc.docx")

其中,test.docx内容如下:


test.docx


生成后的结果如下:


gen


1.4. 案例介绍


1.4.1. 需求假设


写一份不考虑美观的漏扫报告,需要有统计结果图和漏洞详情,每个漏洞包括漏洞名、漏洞地址、漏洞等级、漏洞危害、复现过程、修复建议六个部分。


1.4.2. 模板文档准备


编写的模板文档如下,使用到了常见的iffor赋值等,保存为template.docx,后续只需要向里面填充数据即可。


template


1.4.3. 数据结构分析


传入数据需要一串json字符串,因此我们根据模板文档梳理好json结构,然后传入即可。


梳理好的数据结构如下:


{
"饼图": "111",
"柱状图": "222",
"漏洞简报": [
{
"漏洞名": "测试漏洞名1",
"漏洞等级": "高危"
}
],
"漏洞详情": [
{
"漏洞名": "测试漏洞名1",
"漏洞地址": "http://blog.gm7.org/",
"漏洞等级": "高危",
"漏洞危害": "危害XXX",
"复现过程": "先xxx,再xxx,最后xxx",
"修复建议": "更新到最新版本即可"
}
]
}

编写代码测试一下可行性:


from docxtpl import DocxTemplate

doc = DocxTemplate("template.docx")
context = {
"饼图": "111",
"柱状图": "222",
"漏洞简报": [
{
"漏洞名": "测试漏洞名1",
"漏洞等级": "高危"
},
{
"漏洞名": "测试漏洞名2",
"漏洞等级": "严重"
},
{
"漏洞名": "测试漏洞名2",
"漏洞等级": "中危"
}
],
"漏洞详情": [
{
"漏洞名": "测试漏洞名1",
"漏洞地址": "http://blog.gm7.org/",
"漏洞等级": "高危",
"漏洞危害": "危害XXX",
"复现过程": "先xxx,再xxx,最后xxx",
"修复建议": "更新到最新版本即可"
},
{
"漏洞名": "测试漏洞名2",
"漏洞地址": "http://bblog.gm7.org/",
"漏洞等级": "严重",
"漏洞危害": "危害XXX",
"复现过程": "先xxx,再xxx,最后xxx",
"修复建议": "更新到最新版本即可"
},
{
"漏洞名": "测试漏洞名3",
"漏洞地址": "http://cblog.gm7.org/",
"漏洞等级": "中危",
"漏洞危害": "危害XXX",
"复现过程": "先xxx,再xxx,最后xxx",
"修复建议": "更新到最新版本即可"
}
]
}

doc.render(context)
doc.save("generated_doc.docx")

很好,达到了预期的效果。


res


1.4.4. 加入图表


在上面的过程中,内容几乎是没问题了,但是图表还是没有展示出来。生成图表我们使用plotly这个库,并将生成内容写入ByteIO


相关代码如下:


import plotly.graph_objects as go
from io import BytesIO

def generatePieChart(title: str, labels: list, values: list, colors: list):
"""
生成饼图
https://juejin.cn/post/6911701157647745031#heading-3
https://juejin.cn/post/6950460207860449317#heading-5

:param title: 饼图标题
:param labels: 饼图标签
:param values: 饼图数据
:param colors: 饼图每块的颜色
:return:
"""

# 基础饼图
fig = go.Figure(data=[go.Pie(
labels=labels,
values=values,
hole=.4, # 中心环大小
insidetextorientation="horizontal"
)])
# 更新颜色
fig.update_traces(
textposition='inside', # 文本显示位置
hoverinfo='label+percent', # 悬停信息
textinfo='label+percent', # 饼图中显示的信息
textfont_size=15,
marker=dict(colors=colors)
)
# 更新标题
fig.update_layout(
title={ # 设置整个标题的名称和位置
"text": title,
"y": 0.96, # y轴数值
"x": 0.5, # x轴数值
"xanchor": "center", # x、y轴相对位置
"yanchor": "top"
}
)
image_io = BytesIO()
fig.write_image(image_io, format="png")
return image_io


def generateBarChart(title: str, x: list, y: list):
"""
生成柱状图
https://cloud.tencent.com/developer/article/1817208
https://blog.csdn.net/qq_25443541/article/details/115999537
https://blog.csdn.net/weixin_45826022/article/details/122912484

:param title: 标题
:param x: 柱状图标签
:param y: 柱状图数据
:return:
"""

# x轴长度最为18
b = x
x = []
for i in b:
if len(i) >= 18:
x.append(f"{i[:15]}...")
else:
x.append(i)

# 基础柱状图
fig = go.Figure(data=[go.Bar(
x=x,
y=y,
text=y,
textposition="outside",
marker=dict(color=["#3498DB"] * len(y)),
width=0.3
)])
# 更新标题
fig.update_layout(
title={ # 设置整个标题的名称和位置
"text": title,
"y": 0.96, # y轴数值
"x": 0.5, # x轴数值
"xanchor": "center", # x、y轴相对位置
"yanchor": "top"
},
xaxis_tickangle=-45, # 倾斜45度
plot_bgcolor='rgba(0,0,0,0)' # 背景透明
)
fig.update_xaxes(
showgrid=False
)
fig.update_yaxes(
zeroline=True,
zerolinecolor="#17202A",
zerolinewidth=1,
showgrid=True,
gridcolor="#17202A",
showline=True
)

image_io = BytesIO()
fig.write_image(image_io, format="png")
return image_io

1.4.5. 最终结果


要插入图片内容,代码语法如下:


myimage = InlineImage(tpl, image_descriptor='test_files/python_logo.png', width=Mm(20), height=Mm(10))

完整代码如下:


from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm
import plotly.graph_objects as go
from io import BytesIO


def generatePieChart(title: str, labels: list, values: list, colors: list):
"""
生成饼图
https://juejin.cn/post/6911701157647745031#heading-3
https://juejin.cn/post/6950460207860449317#heading-5

:param title: 饼图标题
:param labels: 饼图标签
:param values: 饼图数据
:param colors: 饼图每块的颜色
:return:
"""

# 基础饼图
fig = go.Figure(data=[go.Pie(
labels=labels,
values=values,
hole=.4, # 中心环大小
insidetextorientation="horizontal"
)])
# 更新颜色
fig.update_traces(
textposition='inside', # 文本显示位置
hoverinfo='label+percent', # 悬停信息
textinfo='label+percent', # 饼图中显示的信息
textfont_size=15,
marker=dict(colors=colors)
)
# 更新标题
fig.update_layout(
title={ # 设置整个标题的名称和位置
"text": title,
"y": 0.96, # y轴数值
"x": 0.5, # x轴数值
"xanchor": "center", # x、y轴相对位置
"yanchor": "top"
}
)
image_io = BytesIO()
fig.write_image(image_io, format="png")
return image_io


def generateBarChart(title: str, x: list, y: list):
"""
生成柱状图
https://cloud.tencent.com/developer/article/1817208
https://blog.csdn.net/qq_25443541/article/details/115999537
https://blog.csdn.net/weixin_45826022/article/details/122912484

:param title: 标题
:param x: 柱状图标签
:param y: 柱状图数据
:return:
"""

# x轴长度最为18
b = x
x = []
for i in b:
if len(i) >= 18:
x.append(f"{i[:15]}...")
else:
x.append(i)

# 基础柱状图
fig = go.Figure(data=[go.Bar(
x=x,
y=y,
text=y,
textposition="outside",
marker=dict(color=["#3498DB"] * len(y)),
width=0.3
)])
# 更新标题
fig.update_layout(
title={ # 设置整个标题的名称和位置
"text": title,
"y": 0.96, # y轴数值
"x": 0.5, # x轴数值
"xanchor": "center", # x、y轴相对位置
"yanchor": "top"
},
xaxis_tickangle=-45, # 倾斜45度
plot_bgcolor='rgba(0,0,0,0)' # 背景透明
)
fig.update_xaxes(
showgrid=False
)
fig.update_yaxes(
zeroline=True,
zerolinecolor="#17202A",
zerolinewidth=1,
showgrid=True,
gridcolor="#17202A",
showline=True
)

image_io = BytesIO()
fig.write_image(image_io, format="png")
return image_io


doc = DocxTemplate("template.docx")
context = {
"饼图": InlineImage(doc, image_descriptor=generatePieChart(
title="漏洞数量",
labels=["严重", "高危", "中危", "低危"],
values=[1, 1, 1, 0],
colors=["#8B0000", "red", "orange", "aqua"]
), width=Mm(130)),
"柱状图": InlineImage(doc, image_descriptor=generateBarChart(
title="漏洞类型",
x=["测试漏洞名1", "测试漏洞名2", "测试漏洞名3"],
y=[1, 1, 1]
), width=Mm(130)),
"漏洞简报": [
{
"漏洞名": "测试漏洞名1",
"漏洞等级": "高危"
},
{
"漏洞名": "测试漏洞名2",
"漏洞等级": "严重"
},
{
"漏洞名": "测试漏洞名2",
"漏洞等级": "中危"
}
],
"漏洞详情": [
{
"漏洞名": "测试漏洞名1",
"漏洞地址": "http://blog.gm7.org/",
"漏洞等级": "高危",
"漏洞危害": "危害XXX",
"复现过程": "先xxx,再xxx,最后xxx",
"修复建议": "更新到最新版本即可"
},
{
"漏洞名": "测试漏洞名2",
"漏洞地址": "http://bblog.gm7.org/",
"漏洞等级": "严重",
"漏洞危害": "危害XXX",
"复现过程": "先xxx,再xxx,最后xxx",
"修复建议": "更新到最新版本即可"
},
{
"漏洞名": "测试漏洞名3",
"漏洞地址": "http://cblog.gm7.org/",
"漏洞等级": "中危",
"漏洞危害": "危害XXX",
"复现过程": "先xxx,再xxx,最后xxx",
"修复建议": "更新到最新版本即可"
}
]
}

doc.render(context)
doc.save("generated_doc.docx")

结果如下:


result


作者:初始安全
来源:juejin.cn/post/7233597845919662139
收起阅读 »

python干饭神器---今天吃什么?python告诉你

一、前言 hello,大家好,我是干饭大王,打工人每天最幸福的时刻莫过于干饭了 😎,然而最纠结的时刻莫过于今天吃什么呢? 😂,于是乎我用python写了一个干饭神器,今天分享给大家,别忘了给我点赞 收藏 评论哟~ 话不多说,先看效果图: 还有隐藏 “福利”...
继续阅读 »

一、前言


hello,大家好,我是干饭大王,打工人每天最幸福的时刻莫过于干饭了 😎,然而最纠结的时刻莫过于今天吃什么呢? 😂,于是乎我用python写了一个干饭神器,今天分享给大家,别忘了给我点赞 收藏 评论哟~


话不多说,先看效果图:
在这里插入图片描述
在这里插入图片描述


还有隐藏 “福利” 帮你的完成减肥大业:


在这里插入图片描述


在本篇博客中,我们将会介绍如何使用Python编写一个带有界面的可切换的轮播图程序。轮播图程序可以从指定的文件夹中读取图片,并可以自动切换到下一张图片。如果图片比较大,则可以进行缩放以适应窗口大小。


二、准备工作


在开始编写程序之前,我们需要安装以下依赖项:



  • tkinter:用于创建GUI界面。

  • Pillow:用于处理图片。


tkinter是python一个内置集成库,Pillow可以使用pip命令来安装:


pip install tkinter Pillow

当安装完成后,我们就可以开始编写程序了。


三、编写代码


说了折磨多,你们是不是非常期待我的代码呀,看代码之前,别忘了先点个关注哟~,接下来,进入代码编写环节


2.1 导入所需的库


首先,我们需要导入所需的库:


import os
import tkinter as tk
from PIL import ImageTk, Image

其中,os库用于读取指定目录下的文件列表;tkinter库用于创建GUI界面;Pillow库用于处理图片。


2.2 创建GUI界面


初始化窗口,并设置窗口的标题,以及窗口的初始大小。


mywindow=tk.Tk()
mywindow.title('今天吃什么')
mywindow.minsize(500,500)

2.3 设置窗口居中显示


我们首先 winfo_screenwidthwinfo_screenheight 函数获取屏幕的尺寸(screenwidth和screenheight),并根据屏幕尺寸计算了窗口的大小和位置。最后使用 geometry 函数 将应用程序窗口设置到屏幕中央。


def getWindowsSize(width, height, screen_width, screen_height):
x = int(screen_width / 2 - width / 2)
y = int(screen_height / 2 - height / 2)
return '{}x{}+{}+{}'.format(width, height, x, y)


screenwidth = mywindow.winfo_screenwidth()
# 得到屏幕宽度
screenheight = mywindow.winfo_screenheight()

print("screenheight:%s,screenheight:%S ",screenheight,screenwidth)

# x tkinter窗口距离屏幕左边距离
mywindow_x = 650
# y tkinter窗口距离屏幕上边距离
mywindow_y = 575
mywindow.geometry(getWindowsSize(mywindow_x,mywindow_y,screenwidth,screenheight))

2.4 设置切换按钮和当前食物的label 以及图片展示 切换方法


photoDefault 用来读取默认首图的图片,imageNameList用来存放 imgs文件下的所有文件路径,nextChoice() 用于当你不满意第一次随机的食物,点击 不满意?换一个 按钮时切换下一个食物。mylab 用来展示图片,eatlab 用来展示食物的中文名,mybut 用于切换。


photoDefault = ImageTk.PhotoImage(Image.open("imgs/今天吃点啥.gif"))

path = 'imgs/'
imagNamelist = os.listdir(path)

def nextChoice():
# 重新排序文件
random.shuffle(imagNamelist)
imagePath=imagNamelist[0]
print('今天吃',imagePath)
finalImg = Image.open("imgs/"+imagePath)
photo = ImageTk.PhotoImage(finalImg.resize((400, 400)))
mylab.config(image = photo)
mylab.image = photo
mybut.config(text='不满意?换一个')
eatlab.config(text=imagePath.split(".")[0])




mylab=tk.Label(master=mywindow,
image= photoDefault
)
mylab.pack()

eatlab=tk.Label(master=mywindow,
text='今天吃点啥?',
fg='white',
bg='green',
font=('微软雅黑',14),
width=20,
height=3
)
eatlab.pack()

mybut=tk.Button(mywindow,
text='点我决定',
font=('微软雅黑',12),
width=15,
height=3,
command=nextChoice
)
mybut.pack()

mywindow.mainloop()

2.5 完整代码


import os
import tkinter as tk
import random
from PIL import Image, ImageTk

mywindow=tk.Tk()
mywindow.title('今天吃什么')
mywindow.minsize(500,500)

def getWindowsSize(width, height, screen_width, screen_height):
x = int(screen_width / 2 - width / 2)
y = int(screen_height / 2 - height / 2)
return '{}x{}+{}+{}'.format(width, height, x, y)


screenwidth = mywindow.winfo_screenwidth()
# 得到屏幕宽度
screenheight = mywindow.winfo_screenheight()

print("screenheight:%s,screenheight:%S ",screenheight,screenwidth)

# x tkinter窗口距离屏幕左边距离
# mywindow_x = mywindow.winfo_x()
# # y tkinter窗口距离屏幕上边距离
# mywindow_y = mywindow.winfo_y()

# x tkinter窗口距离屏幕左边距离
mywindow_x = 650
# y tkinter窗口距离屏幕上边距离
mywindow_y = 575
mywindow.geometry(getWindowsSize(mywindow_x,mywindow_y,screenwidth,screenheight))


photoDefault = ImageTk.PhotoImage(Image.open("imgs/今天吃点啥.gif"))

path = 'imgs/'
imagNamelist = os.listdir(path)

def nextChoice():
# 重新排序文件
random.shuffle(imagNamelist)
imagePath=imagNamelist[0]
print('今天吃',imagePath)
finalImg = Image.open("imgs/"+imagePath)
photo = ImageTk.PhotoImage(finalImg.resize((400, 400)))
mylab.config(image = photo)
mylab.image = photo
mybut.config(text='不满意?换一个')
eatlab.config(text=imagePath.split(".")[0])




mylab=tk.Label(master=mywindow,
image= photoDefault
)
mylab.pack()

eatlab=tk.Label(master=mywindow,
text='今天吃点啥?',
fg='white',
bg='green',
font=('微软雅黑',14),
width=20,
height=3
)
eatlab.pack()

mybut=tk.Button(mywindow,
text='点我决定',
font=('微软雅黑',12),
width=15,
height=3,
command=nextChoice
)
mybut.pack()

mywindow.mainloop()

四、打包exe运行程序,方便在其他windows电脑上使用


4.1 安装pyinstaller打包程序


要想打包成windows下可以运行的 exe 可以使用pip安装如下依赖包,


pip3 install pyinstaller

4.2 执行打包


进入我们的源代码目录,比如我的就是:D:\project\opensource\t-open\python-labs\src\eat , 然后执行如下命令即可, eat2.py 是我们写的python源代码,eatSomething.exe 是我们最后打完包的程序名称。


pyinstaller -F -w eat2.py -n eatSomething.exe

在这里插入图片描述


4.3 移动img文件,运行程序


打包完成后 会在 dist目录生成 eatSomething.exe 应用程序,由于打包的时候我们并没有把 imgs 文件夹的图片拷贝进去,所以我们可以手动把 imgs 全部拷贝到 dist 文件夹下,当然你也可以 把你喜欢的食物照片放到 imgs 文件夹下面。
在这里插入图片描述
在这里插入图片描述
然后我们就可以双击 eatSomething.exe
在这里插入图片描述


祝大家不在纠结今天吃什么,做一个果断 坚毅 的人✌️✌️✌️
如果你喜欢的话,就不要吝惜你的一键三连了~ 🤝🤝🤝
谢谢大家!


这里是文章的表白神器所有代码+图片,对文章不是太懂得小伙伴们可以自取一下哟:今天吃点啥的源代码地址:



  1. 码云地址gitee.com/T-OPEN/pyth…

  2. 源代码和程序下载地址download.csdn.net/download/we…


作者:TOPEN
来源:juejin.cn/post/7231526222634582071
收起阅读 »

python-实现地铁延误告警

在深圳地铁延误、临停n次之后 终于让我不得不又new了一个py文件😭😭 这次主要记录的是一个延误告警的开发过程 一、实现逻辑 使用库:requests,time,zmail,re 实现逻辑: 1、抓取深圳地铁微博的文章 2、判断是否有延误相关的内容 3、判断时...
继续阅读 »

在深圳地铁延误、临停n次之后


终于让我不得不又new了一个py文件😭😭


这次主要记录的是一个延误告警的开发过程


一、实现逻辑


使用库:requests,time,zmail,re


实现逻辑:


1、抓取深圳地铁微博的文章


2、判断是否有延误相关的内容


3、判断时间是否是今天

4、通知方式:邮件


5、定时执行任务


二、抓取深圳地铁微博(一中1~3)



def goout_report():
url ="https://weibo.com/ajax/statuses/mymblog"
# url ="https://weibo.com/szmcservice/statuses/mymblog"
data = {"uid":2311331195,"page":1,"feature":0}
headers={
"accept":"application/json, text/plain, */*",
"accept-encoding":"gzip, deflate, br",
"accept-language":"zh-CN,zh;q=0.9",
"referer":"https://weibo.com/szmcservice?tabtype=feed",
"cookie":"SUB=_2AkMV8LtUf8NxqwJRmf8XzmLgaY9wywjEieKjrEqPJRMxHRl-yT92ql0ctRB6PnCVuU8iqV308mSwZuO-G9gDVwYDBUdc; SUBP=0033WrSXqPxfM72-Ws9jqgMF55529P9D9WFpwsXV4nqgkyH.bEVfx-Xw; login_sid_t=c6bbe5dc58bf01c49b0209c29fadc800; cross_origin_proto=SSL; _s_tentry=passport.weibo.com; Apache=4724569630281.133.1655452763512; SINAGLOBAL=4724569630281.133.1655452763512; ULV=1655452763517:1:1:1:4724569630281.133.1655452763512:; wb_view_log=1920*10801; XSRF-TOKEN=1YMvL3PsAm21Y3udZWs5LeX3; WBPSESS=xvhb-0KtQV-0lVspmRtycws5Su8i9HTZ6dAejg6GXKXDqr8m6IkGO6gdtA5nN5IMNb5JZ1up7qJoFXFyoP2RSQSYXHY1uLzykpOFENQ07VthB0G9WHKwRCMWdaof42zB4mOkdTEeX_N9-m1x6Cpm3pmPsC1YhmTwqH8RGwXmYkI=",
"referer":"https://weibo.com/szmcservice",
"x-requested-with": "XMLHttpRequest",
"x-xsrf-token":"1YMvL3PsAm21Y3udZWs5LeX3",
"sec-ch-ua":'Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102',
"sec-ch-ua-platform":"Windows",
"sec-fetch-dest": "empty",
}
text = requests.get(url,headers=headers,params=data,verify=False).json()['data']['list']
today_date = time.ctime()[:10]
for i in range(1,5):
time_post = text[i]['created_at'][:10]
content = str(text[i]).split("'text': '")[1].split(", 'textLength'")[0]
tp=""
if '延误' in content and time_post == today_date:
# mail(content)
text = re.findall(">(.*?)<|>(.*?)\\",content)
for i in text:
for j in i:
if j!="":


                       tp=tp+j

        mail(tp)
break
else:
continue



三、邮件通知,代码如下


def mail(content):
mail = {
'subject': '别墨迹了!地铁又双叒叕延误啦', #邮件标题
'content_text': content, # 邮件内容
}
server = zmail.server('自己的邮箱', '密码',smtp_host="smtp.qq.com",
smtp_port=465) #此处用的qq邮箱、授权码
server.send_mail('收件人邮箱', mail)

ps:需去QQ邮箱网页版-设置-账户-开启smtp服务、获取授权码


四、定时执行任务


1、Jenkins比较合适项目的一个定时执行,


可参考如下:


jenkins环境: jenkins环境部署踩坑记


git环境:Mac-git环境搭建


2、windows-计算机管理比较合适脚本的执行,具体步骤如下,




  • windows键+R输入compmgmt.msc可进入计算机管理界面


    图片




  • 点击上图“创建任务”后如图,


    “常规”界面上输入任务名称、选项二,


    这样锁屏也会自动执行脚本


    图片




  • 点击“触发器”-新建进入新建触发器界面


    这个界面可设置任务执行时间、执行频率、任务重复间隔、延迟时间等等


    图片




  • 点击“操作”-新建跳到如图-新建操作界面


    这个界面可在“程序或脚本”输入框设置脚本运行程序,比如python.exe


    在“添加参数”输入框设置需要运行脚本路径(包含脚本名)


    在“起始于”输入框设置脚本执行路径(一般可为脚本目录)


    图片




  • 其他选项卡也可以看看,


    全部填写完可以点击“创建任务”界面上的“确定”按钮,


    然后在列表中找到新建的任务点击可查看,


    图片




  • 实时执行测试的话可以点击上图“运行”按钮


    或者右击任务-运行即可


    任务执行结果如下:




图片


作者:WAF910
来源:juejin.cn/post/7231074060788613175
收起阅读 »

《左手上篮》之弹幕含‘’坤‘’量分析?!

对不起 别骂了别骂了我有错,但是我不认。哈哈哈 本来就是想爬一下最近比较火的国产动漫《左手上篮》,我是一个篮球爱好者 ,也是一个篮球迷,有这种篮球的国漫怎么会放过呢,所以我也想搞点事情分析分析弹幕,其实我有想过一个比较好的题目《左手上篮》--我们的灌篮高手,其...
继续阅读 »

对不起


别骂了别骂了我有错,但是我不认。哈哈哈


本来就是想爬一下最近比较火的国产动漫《左手上篮》,我是一个篮球爱好者 ,也是一个篮球迷,有这种篮球的国漫怎么会放过呢,所以我也想搞点事情分析分析弹幕,其实我有想过一个比较好的题目《左手上篮》--我们的灌篮高手,其实没开始爬之前我一直是这么想的,但是当我真正去爬的时候发现一个这样的弹幕‘‘123,背带裤’’事情就开始变得不一样了,我想正常的一板一眼的做弹幕数据分析是不是太无聊了,所以我决定做弹幕的含坤量分析,这就是我标题的来源。


爬取弹幕


image.png


上面这个就是我的爬虫代码了,其实非常简单,就是一个请求头,一个request函数,然后在爬的视频网站找到你要的json包,给他请求解析下来,然后写到我们的CSV文件中。下面就是我爬到的数据大概有两万条弹幕


image.png


数据处理


接着就是对爬到的数据进行中文分词,把弹幕用jieba分好词,大概有80万条小数据,我做的第一个处理是把他做一个词云图,通过对停用词的不同限定,做了几个版本的词云图,为什么做了几个版本呢,其实是被迫的,本来我早就开始这个项目了,就是在这里被卡了很久,不然早就做完了,主要就是stylecloud这个库不太熟悉,所以一直画不出来,第一个词云图其实是我用fineBI做的,直到今天有空了,所以好好研究了下,终于不报错了谢天谢地。其实画的还是很粗糙,大家将就看吧。


屏幕截图 2023-03-16 140620.png


image.png


image.png


含坤量分析


接着有趣的来了,我们来看一下我们鸡哥在这些弹幕里的含量,首先我们在弹幕中把含坤的弹幕统计出来,words = ["坤", "背带裤", "小黑子", "ikun", "蔡徐坤", "只因", "鸡", "鸡你太美"],这些都是我们的含坤的弹幕类型,我们对弹幕进行筛选,有这些词的我们就把他放到一起去。


image.png


大概有多少呢?如图所示一共236条。接着我们对他进行数据的可视化,我分别做了柱状图和一个饼状图。


image.png


image.png


OK,最后一步含坤量的计算最后的结果是:


0.02906%


怎么算的呢?其实很简单就是用我们筛选出来的词比上我们全部的词。


image.png


结尾


以上内容,如有雷同纯属巧合,如有冒犯就是你对。


作者:HeteroCat
来源:juejin.cn/post/7221521544496873528
收起阅读 »

python | 写一个记仇本

最近背着老婆买了一个switch卡带,这货居然给丈母娘讲,害得我被丈母娘说还小了,不买奶粉买游戏,太气人了,我连夜用python写了个《记仇本》,画个圈圈把她记下来。 本片文章,主要关注于python代码,而html和css将暂时被忽略。 记仇本展示 如题所...
继续阅读 »

最近背着老婆买了一个switch卡带,这货居然给丈母娘讲,害得我被丈母娘说还小了,不买奶粉买游戏,太气人了,我连夜用python写了个《记仇本》,画个圈圈把她记下来。


本片文章,主要关注于python代码,而htmlcss将暂时被忽略。



记仇本展示


如题所述,项目已经写好了,是基于local_storage存储在本地的项目,地址如下:



该项目运行时是基于brython, 你可能想问,为什么不使用原生python来编写网页呢,这个有个误区是,网页是由html代码编写的,而原生python想要操作DOM非常难,所以brython正是为这个来操作的。


初始打开页面,因为没有数据展示,所以只有一个增加按钮。



当我们点击【画个圈圈记下来】按钮后,会刷新为新增页面,例如:



此时,我们只需要输入信息,比如 记老婆的仇,缘由为 买switch游戏透露给丈母娘,还得被骂。



此时点击记仇,就可以存储到页面上了。



此时若点击已原谅,则可以删除该记录。


brython 之 local_storage


你可能细心发现了,哎,关掉了浏览器,下次打开,怎么还会有记录在上面呢,这是因为用了local_storage,那么,什么是local_storage呢?


哎,我们使用的是brython中的local_storage但是,它可不是python定义的哦,而是HTML 5提供的存储数据的API之一,可以在浏览器中保持键值对数据块。


现在来展示使用一下brython存储和删除的操作。


导入库:


from browser.local_storage import storage

存储数据,例如键值信息juejinName存储为pdudo


storage[juejinName] = "pdudo"

查询的话,直接使用storage[变量]就好,若为空,则返回


v = storage[juejinName]

循环所有的key,需要引入window库,再使用for...in来完成


from browser import window
for key in window.localStorage:
print(key)

也可以直接使用for...in来遍历storage


而删除数据呢?只需要像删除字典一下


del storage[juejinName]

storage是不是操作起来和字典非常类似呢?减少了开发者的学习成本。


上述案例,已经放到下面的链接中了:



制作项目


有了上述前置条件后,我们再看该项目,便可以总结为 针对localStorage的增删查,首先当页面加载出来的时候,我们需要先遍历一下localstorage数据,从而映射为一个table,例如:


  for key in window.localStorage:
tr = html.TR()
datas = json.loads(storage[key])

delBtn = html.BUTTON("已原谅")
delBtn.dataset["id"] = datas["id"]
delBtn.className = "confirm-btn"
delBtn.bind("click",delGrudges)

td = html.TD(delBtn+" "+time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(datas["id"]))))
tr <= td

for tdVal in datas["whos"],datas["Text"]:
td = html.TD(tdVal)
tr <= td

tb <= tr

userWindows <= tb

上述代码是遍历localStorage,而后在定义删除按钮,等将其他值组合完毕后,全部加载进table中,而后再页面上显示。


而添加数据呢?


def saveGrudges(ev):
getWhoVal = document["whos"].value
getTextVal = document["textArea"].value

if getWhoVal == "" or getTextVal == "":
return

document["saveBtn"].unbind("click")


ids = int(time.time())
datas = {
"id": ids,
"whos": getWhoVal,
"Text": getTextVal
}

storage[str(ids)] = json.dumps(datas)

上述代码,显示获取inputtextarea框中的值,再判断是否用户没有输入,我们将数据组装为一个字典,而后转换为字符串,再存入localstage中。


还有其他操作,这个可以直接看代码说明,brython很简单的。


总结


这篇文章,是善用localStorage来作为键值对存储,以此来保证打开和关闭浏览器,不会对数据产生影响,整个项目就是围绕这个localStorage增删改查来操作的。


作者:真的不能告诉你我的名字
来源:juejin.cn/post/7222229682027462693
收起阅读 »

看我如何用定值 Cookie 实现反爬

本文分享自华为云社区《我是怎么用一个特殊Cookie,限制住别人的爬虫的》,作者: 梦想橡皮擦 。 Cookie 生成 由于本案例需要用到一个特定的 Cookie ,所以我们需要提前将其生成,你可以直接设置一个固定的字符串,也可以使用 Python 加密模块来...
继续阅读 »

本文分享自华为云社区《我是怎么用一个特殊Cookie,限制住别人的爬虫的》,作者: 梦想橡皮擦 。


Cookie 生成


由于本案例需要用到一个特定的 Cookie ,所以我们需要提前将其生成,你可以直接设置一个固定的字符串,也可以使用 Python 加密模块来加密一段文本,例如本案例加密 梦想橡皮擦


下面是一个示例代码,展示了如何使用 Python 的 hashlib 模块进行加密:


import hashlib

# 要加密的文本
text = "梦想橡皮擦"

# 使用 sha256 算法进行加密
encrypted_text = hashlib.sha256(text.encode()).hexdigest()

print(encrypted_text)

在这个例子中,我们使用了 hashlib 模块中的 sha256 算法对文本进行加密。这个算法生成了一个长度为 64 位的十六进制哈希值,用于表示加密后的文本。



注意,这个算法只能用于加密文本,而不能用于解密。因此,一旦文本被加密,就无法恢复成原来的文本,即不可逆加密/单项加密。



Python Flask 框架生成 Cookie


在 Python 的 Flask 框架中,可以使用 make_response 函数和 set_cookie 方法来生成一个 Cookie。


例如,下面的代码片段展示了如何在 Flask 中设置一个名为 story 的 Cookie,并将它的值设为前文建立的加密串。


from flask import Flask, make_response

app = Flask(__name__)

@app.route('/')
def index():
resp = make_response('Setting a cookie')
encrypted_text = hashlib.sha256(text.encode()).hexdigest()
resp.set_cookie('story', encrypted_text)

return resp

在这个例子中,我们使用 make_response() 函数创建了一个响应对象,然后使用 set_cookie() 方法来设置 cookie。最后,我们将响应对象返回给客户端。


注意,上面的代码仅创建了一个简单的 Cookie,它只有名称和值两个部分。你还可以使用其他可选参数来设置 Cookie 的其他属性,例如过期时间、域名等。


接下来为大家在补充一下 make_response() 相关知识。


Flask make_response 加载模板


在 Flask 中,你可以使用 make_response() 函数和模板系统来生成带有模板的响应。


下面是一个示例代码,展示了如何使用 make_response() 函数加载模板:


from flask import Flask, make_response, render_template

app = Flask(__name__)

@app.route('/')
def index():
# 加载模板并渲染
rendered_template = render_template('index.html', title='梦想橡皮擦')
# 使用 make_response 函数创建响应
resp = make_response(rendered_template)
return resp

在这个例子中,我们首先使用 Flask 的 render_template() 函数加载并渲染了名为 index.html 的模板。然后我们使用 make_response() 函数创建了一个响应对象,并将渲染后的模板作为响应的内容。最后,我们返回了这个响应对象给客户端。



注意,你需要在 Flask 应用的模板目录中存在名为 index.html 的模板文件,才能正常使用上述代码。



然后我们将该视图函数补充完整,代码在 app/routes.py 文件中。


@app.route('/')
@app.route('/index')
def index():
item = {
"msg": "后台传递信息"
}
# 访问首页生成一个 Cookie 值,该值用于访问特定页面
rendered_template = render_template('index.html', title='梦想橡皮擦')
resp = make_response(rendered_template)
text = "梦想橡皮擦"

# 使用 sha256 算法进行加密
encrypted_text = hashlib.sha256(text.encode()).hexdigest()
resp.set_cookie('story', encrypted_text)
return resp

此时当我们访问爬虫训练场首页的时候,就会在 Cookie 中写入一个加密之后的字符串。

通过开发者工具,可以查看到响应头。



最后一步,就是在 Python Flask 框架中判断刚刚的 Cookie 值,如果存在则响应数据,否则返回 403。


Flask 判断指定 cookie 是否存在


在 Python 的 Flask 框架中,你可以使用 request.cookies 属性来判断指定的 Cookie 是否存在。


例如,下面的代码片段展示了如何判断一个名为 story 的 Cookie 是否存在:


from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def index():
if 'story' in request.cookies:
# 如果存在 'story' cookie,则执行相应操作
# ...
else:
# 如果不存在 'story' cookie,则执行相应操作
# ...

将代码补充完整,文件是 app/antispider/index.py


@antispider.route('/cookie_demo')
def cookie_demo():
if 'story' in request.cookies:
# 如果存在 'story' cookie,则执行相应操作
# ...
return render_template("antispider/cookie_demo.html")
else:
return "没有权限", 403

补充知识点


在 Python 的 Flask 框架中,除了使用 set_cookie() 方法设置 cookie 以外,还有其他几种操作 cookie 的方法。


下面是一些常用的操作 cookie 的方法:




  • 设置 cookie 的值:你可以使用 set_cookie() 方法来设置 cookie 的值。例如:


    from flask import Flask, make_response


    app = Flask(name)


    @app.route('/')
    def index():
    resp = make_response('Setting a cookie')
    resp.set_cookie('user', 'xiangpica')
    return resp




  • 获取 cookie 的值:你可以使用 request.cookies字典来获取 cookie 的值。例如:


    from flask import Flask, request


    app = Flask(name)


    @app.route('/')
    def index():
    user = request.cookies.get('user')
    return user




  • 删除 cookie:你可以使用 set_cookie() 方法并将 cookie 的过期时间设为过去的时间来删除 cookie。例如:


    from flask import Flask, make_response


    app = Flask(name)


    @app.route('/')
    def index():
    resp = make_response('Deleting a cookie')
    resp.set_cookie('user', '', expires=0)
    return resp




点击关注,第一时间了解华为云新鲜技术~


作者:华为云开发者联盟
来源:juejin.cn/post/7217665415710244921
收起阅读 »

为什么 Python、Go 和 Rust 都不支持三元运算符?

在编程时,我们经常要作条件判断,并根据条件的结果选择执行不同的语句块。在许多编程语言中,最常见的写法是三元运算符,但是,Python 并不支持三元运算符,无独有偶,两个最热门的新兴语言 Go 和 Rust 也不支持! 为什么 Python 不支持三元运算符呢?...
继续阅读 »

在编程时,我们经常要作条件判断,并根据条件的结果选择执行不同的语句块。在许多编程语言中,最常见的写法是三元运算符,但是,Python 并不支持三元运算符,无独有偶,两个最热门的新兴语言 Go 和 Rust 也不支持!


为什么 Python 不支持三元运算符呢?本文将主要分析 Python 在设计条件选择语法时的过程,科普为什么它会采用现今的与众不同的实现方案,同时,我们也将考察为什么其它语言也要抛弃传统的三元运算符。


在开篇之前,我再声明一下:就像“Python为什么”系列的大部分文章一样,本文关注的仅是一个很小的语法点,但它并不是“茴香豆有几种写法”那种毫无意义的话题。因为,细微之处见真功夫,深入研究语言设计背后的原因、历史和哲学,可以让我们在编程时有更加清晰和自由的思维。


什么是三元运算符?


三元运算符通常指的是“?:”,其语法形式为:condition ? expression1 : expression2,如果 condition 为真,则取 expression1,若不为真,则取 expression2。


语法简化形式“a ? b : c”,可以读成“如果 a 条件成立,则为 b,否则为 c”。


三元运算符是对普通一重 if-else 结构的简化,常用于在一条语句中同时实现条件判断和取值操作。


// 常规 if-else 
if (a > b) {
result = x;
} else {
result = y;
}

// 简化后的写法
result = a > b ? x : y;

采用了这种语法设计的编程语言有很多,比如 C、C#、C++、Java、JavaScript、PHP、Perl、Ruby、Swift 等等。毫无争议,它就是编程语言界的主流设计方案(至今仍是)。


这种语法非常简洁高效,代码的可读性也很强(如果你不是第一次接触的话),深得很多人的喜欢。


但是,它并非毫无缺点。Python 是这种语法设计的最著名的挑战者,接下来,我们将看看为什么 Python 要另辟蹊径。


Python 社区的投票


Python 发布于 1991 年,但在接下来的 15 年里,除了 if-else 语法外,它并不支持三元运算符和其它条件表达式。而且,在 2006 年引入条件表达式前,社区对此进行了漫长而曲折的争论,可以说这是一个设计得很艰难的语法了。


最初,由于时常有人请求添加 if-then-else(三元)表达式,因此在 2003 年 2 月,PEP 308 – Conditional Expressions 被提了出来,目的是让社区选出一个让多数人支持的方案。


PEP-308


很快,除了少部分人希望啥也不做外,社区里出现了好几种方案:


(1)使用标点符号构建的三元运算符


即常规的三元运算符,跟前文介绍的语法一样:


<condition> ? <expression1> : <expression2>

这个方案的呼声挺高,有开发者甚至已提交了实现代码。但是,Guido 给出了两个反对的理由:冒号在 Python 中已经有许多用途(即使它实际上不会产生歧义,因为问号需要匹配冒号);对于不习惯 C 衍生语言的人来说,理解起来很困难。


(2)使用现有和新的关键字构建


引入新的“then”关键字,结合现有的“else”关键字:


<condition> then <expression1> else <expression2>

它的优点是简单明了、不需要括号、不改变现有关键字的语义,不大可能与语句混淆,而且不需要重载冒号。缺点是引入新关键字的实现成本较高。


(3)其它思路


跟上一种方案的思路相似,但没有上述两类方案的支持度高。


(if <condition>: <expression1> else: <expression2>)
<condition> and <expression1> else <expression2>
<expression1> if <condition> else <expression2>
cond(<condition>, <expression1>, <expression2>)

值得一提的是(if <condition>: <expression1> else: <expression2>) ,它是常规 if-else 语法的扁平化,容易理解,但缺点是需要使用圆括号,容易跟生成器表达式混淆,而且需要解释器对冒号做特殊化处理。


另外值得一提的是<expression1> if <condition> else <expression2>,它是 PEP-308 最早版本的推荐方案,但是这种不将条件放在首位的风格让一些人感觉不舒服,而且,当“expression1”很长的时候,很容易就忽略掉它的条件。


当时参与投票的全部设计方案:



总体上,开发者们希望引入某种形式的 if-then-else 表达式,但投票后却没有哪种方案能取得绝对的优势。概括起来,分歧的问题主要有:是否用标点符号、是否复用关键字、是否复用圆括号、是否引入新关键字、是否引入新语法……


由于得票太分散,因此,这个 PEP 在当时被拒绝了。PEP 中写道:“Python 的一个设计原则是在不确定采取哪条路线时,则保持现状。


and-or 用于条件选择的问题


以上的投票事件发生在 2004 年 3 月,但是,在 PEP 被拒绝后,相关话题的讨论并未平息,因为大家总想找一种简洁的方式来替换“if-else“。


时间到了 2005 年 9 月,邮件组中有人提议在 Py3.0 中变更"and"与"or"操作符的逻辑,提议将"and" 和 "or" 运算符简化成始终返回布尔值,而不是返回最后一个被求值的参数。


之所以发起这个提议,原因是他使用了<condition> and <expression1> or <expression2>的方式来实现条件判断与选择。但是这种写法在 Python 中的行为跟有些语言并不一样,使用不严谨的话,可能会酿成 Bug!


看看下面的两个例子,你觉得它们会得到什么结果呢?


a = True and True or "Python猫"

b = True and False or "Python猫"

对于<condition> and <expression1> or <expression2> ,若 condition 为假,则会直接对 expression2 求值并返回结果;若 condition 为真,则先对 expression1 求值,若也为真,则不会继续对 expression2 求值,若 expression1 不为真,则对 expression2 求值。


因此,上述例子得到的 a 是“True”,而 b 会得到“Python猫”。


本系列的《Python 为什么能支持任意的真值判断? 》介绍过 Python 在真值判断的特殊之处,运用到以上结构中,将出现更不易察觉的问题。比如,该邮件的作者就是遇到了“expression1”为复数“0+4i”,这个数的真值判断为 False,因此导致最后返回的不是预期的“expression1”,而是“expression2”!


在没有更好的方案前,“and-or”是比较常见的条件选择写法,PEP-308 也提及了它,也指出了当“expression1”为假的情况,还认为这种方案是丑陋和令人费解的。


这封邮件再次引发了社区对条件选择语法的讨论,大佬们纷纷登场。


以我现在的视角分析,其实就是开发者们不满足于“if-else”的现状,但是当时流行的“and-or”写法并不够好,因此,大家期望 Python 设计出新的规范性语法,来解决这个痛点。


与众不同的条件表达式


在经过 10 天的邮件讨论后,Guido van Rossum 最终决定添加一个条件表达式,语法形式为X if C else Y 。因此,PEP-308 被重开和更新,并很快就在次年的 2.5 版本中实现了。


前文已提到过这个让一些人感觉不舒服的方案了,因为它没有将条件判断逻辑放在最前面。


那么,为什么最后的胜者会是它呢?这是不是最优的设计呢?


不可否认,起到决定性作用的原因是 Guido。由于社区在一年半前投票时没有形成多数意见,因此他行使 BDFL (终身仁慈独裁者)的决策权力,裁定出一个他认为是最佳的方案。


X if C else Y 非常易于理解,可读性高。它延续了“明确优于隐式”的风格,使用了直观口语化的“if-else”,而不是引入可能引起混淆的标点符号,就像 Python 选择“and”和“or”两个单词,而不是“&&”和“||”两个符号,它们有着异曲同工之妙。


虽然调整后的语法顺序让人不太习惯,但其实这样的实现却大有好处。首先,它只需复用“if-else”两个关键字,而不需要引入“then”、“when”和其它语法要素,也不像(if <condition>: <expression1> else: <expression2>) 那样的繁琐。


其次,为了验证X if C else Y 的有效性,Guido 排查了标准库中所有“and-or”组合的写法,发现那些C and X or Y 写法都可以被X if C else Y 替换掉。标准库的情况,证明了这新的语法是可行的。


最后,在 PEP-308 提及的原因外,我还想补充一点。据观察,我发现很多时候我们有一个已初始化的变量,然后需要在出现某个条件时,更新变量的值。在这种情况下,“else”部分可以被省略,非常便捷。


my_str = ""
# 中间存在其它代码逻辑
# 当 condition 为真时,变量会被重新赋值
my_str = "Python猫" if condition

回顾这段历史,我们可以梳理出一条线索:Python 没有设计三元运算符“?:”,主要是因为它不符合 Python 明确直观的设计风格。最后采用X if C else Y 这种设计,主要的意图其实是消除“and-or”写法的隐患,这种设计简明易读,而且还有<expression> if <condition> 简化写法的妙用。


总体而言,Python 设计者非常看重可读性与可维护性,不采用三元运算符而创造条件表达式语法,这是一个经过了开放讨论、谨慎评估与权衡取舍的结果。


Go、Rust 为什么不支持三元运算符?


考察完 Python 的设计原因后,我们再来考察“反派阵营”中两门最热门的语言。


首先是 Go 语言,官网的 FAQ 专门列出了一个问题:“Why does Go not have the ?: operator?”。


Go 语言不支持“?:”运算符,而是推荐使用原生的“if-else”写法。文档的解释很简短,只有一段话:



Go 语言没有 ?: 运算符,因为语言的设计者们经常看到它被用来创建难以理解的复杂表达式。虽然 if-else 形式比较长,但是它无疑更清晰易懂。一个语言只需要一个条件控制流结构



接着是 Rust 语言,它的官方文档中似乎没有任何关于不支持三元运算符的解释。但在查阅资料后,我发现它也有一段特殊的故事,非常有意思:在 2011 年 6 月时,Rust 曾经引入过三元运算符(#565),然而半年后,设计者意识到这个特性是多余的,因此又把它移除了(#1698#4632)!


为什么三元运算符在 Rust 是多余的呢?因为它的 if 语法并不像其它语言是“语句(statement)”,而是一个“表达式(expression)”,这意味着你可以直接将 if 表达式赋值给变量:


// 若条件为真,得到 5,否则 6
let number = if condition { 5 } else { 6 };

这种语法形式足够简单明了,不就是将大家都熟悉的“if-else”直接用于赋值么,太方便了,替换成三元运算符的话,确实有点画蛇添足之感。


另外,Rust 使用花括号划分代码块,因此上例的花括号内可以包含多条表达式,也支持换行,例如这个例子:


let x = 42;
let result = if x > 50 {
println!("x is greater than 50");
x * 2 // 这是一个表达式,将返回的值赋给 result
} else {
println!("x is less than or equal to 50");
x / 2 // 也是一个表达式,将返回的值赋给 result
};

这种用法,Python 是不可能做到的。最关键的区别在于,Rust 的 if 是表达式而不是语句。


这两个概念的区别是:



  • 表达式(expression)通常指的是由变量、常量、运算符等组成的一个可求值的代码片段,它的求值结果可以用到其它表达式或语句中。

  • 语句(statement)通常指的是完成某个任务的单个指令或一组指令,例如赋值语句、条件语句、循环语句等,它没有返回值(或者为空),不能用于赋值操作。


除了 Rust 外,还有一些编程语言中的 if 是表达式而不是语句,例如 Kotlin、Scala、F#、Swift,它们在理论上也不需要使用三元运算符。(题外话:Swift 是个例外,它也有三元运算符。Kotlin 有“?:”运算符,注意两个符号是连在一起的,val result = a ?: b 表示:如果 a 不为 null,则赋值给 result ;否则将 b 赋给 result


由于有这种语言设计层面的区别,因此在面对“是否要支持三元运算符”这个问题时,Rust 和 Python/Go 的思考角度有着天然不同的起点。知道了这种区别后,我们对编程语言会有更明晰地认知。


回到本文的问题:为什么有些编程语言不采用主流的三元运算符语法呢?


不可否认,“?:”确实是一种简洁好用的设计,然而,标点符号的负面影响是过于抽象,可读性并不及“if-else”那样强。另外,不同语言的设计风格与使用习惯,也会导致不同的选择。


Python 在经过一番波折后,最后设计出了与众不同的条件表达式。Go 语言明确表示不支持三元运算符。Rust 先设计后舍去,主要的原因在于 if 表达式的语言基础。


考察完这三个热门语言后,我相信你已收获了一个满意的答案。如果是这样,请点赞支持一下本文吧!


最后,本文出自“Python为什么”系列,全部文章已归档在 Github 上,欢迎 star

作者:豌豆花下猫
来源:juejin.cn/post/7217755581847846967
和提 issue。

收起阅读 »

大喊一声Fuck!代码就能跑了是什么体验?

大喊一声Fuck!代码就能跑了是什么体验? 1 前言 大家好,我是心锁,23届准毕业生。 程序员的世界,最多的不是代码,而是💩山和bug。 近期我在学习过程中,在github找到了这么一个项目,能在我们输错命令之后,大喊一声Fuck即可自动更正命令,据说喊得越...
继续阅读 »

大喊一声Fuck!代码就能跑了是什么体验?


1 前言


大家好,我是心锁,23届准毕业生。


程序员的世界,最多的不是代码,而是💩山和bug。


近期我在学习过程中,在github找到了这么一个项目,能在我们输错命令之后,大喊一声Fuck即可自动更正命令,据说喊得越大声效果越好。


c37237b03e45fed8c2828c6f7abb93b9


2 项目基本介绍


thefuck是一个基于Python编写的项目,它能够自动纠正你在命令行中输入的错误命令。如果你输错了一个命令,只需要在命令行中输入“fuck”,thefuck就会自动纠正你的错误。该项目支持众多的终端和操作系统,包括Linux、macOS和Windows。


43885f5e1f8c7ff2b3392d297c855609


2.1 环境要求



  • python环境(3.4+)


2.2 安装方式


thefuck支持brew安装,非常方便,在macOS和Linux上都可以通过brew安装。


brew install thefuck

也支持通过pip安装,便携性可以说是一流了。


pip3 install thefuck

2.3 配置环境变量


建议将下边的代码配置在环境变量中(.bash_profile.bashrc.zshrc),不要问为什么,问就是有经验。


eval $(thefuck --alias)
eval $(thefuck --alias FUCK)
eval $(thefuck --alias fuck?)
eval $(thefuck --alias fuck?)

接着运行source ~/.bashrc(或其他配置文件,如.zshrc)确认更改立即可用。


3 使用效果


Untitled


03cf7e926946b7d8a3da902841c3c5b1


4 thefuck的工作原理


thefuck的工作原理非常简单。当你输入一个错误的命令时,thefuck会根据你输入的命令和错误提示自动推测你想要输入的正确命令,并将其替换为正确的命令。thefuck能够自动推测正确的命令是因为它内置了大量的规则,这些规则能够帮助thefuck智能地纠正错误的命令。


所以,该项目开放了自定义规则。


4.1 创建自己的规则


如果thefuck内置的规则不能够满足你的需求,你也可以创建自己的规则。thefuck的规则是由普通的Python函数实现的。你可以在~/.config/thefuck/rules目录下创建一个Python脚本,然后在其中定义你的规则函数。


以创建一个名为my_rule的规则为例,具体步骤如下:


4.1.1 创建rule.py文件


~/.config/thefuck/rules目录下创建一个Python脚本,比如my_rules.py


4.1.2 遵循的规则


在自定义脚本中,必须实现以下两个函数,match显然是用来匹配命令是否吻合的函数,而get_new_command则会在match函数返回True时触发。


match(command: Command) -> bool
get_new_command(command: Command) -> str | list[str]

同时可以包含可选函数,side_effect的作用是开启一个副作用,即除了允许原本的命令外,你可以在side_effect做更多操作。


side_effect(old_command: Command, fixed_command: str) -> None

5 yarn_uninstall_to_remove


以创建一个名为yarn_uninstall_to_remove的规则为例,该规则会在我们错误使用yarn uninstall …命令时,自动帮助我们修正成yarn remove … 。具体步骤如下:


5.1 创建yarn_uninstall_to_move.py文件


~/.config/thefuck/rules目录下创建一个Python脚本,yarn_uninstall_to_remove.py


5.2 编写代码


from thefuck.utils import for_app

@for_app('yarn')
def match(command):
return 'uninstall' in command.script

def get_new_command(command):
return command.script.replace('uninstall', 'remove')

priority=1 # 优先级,数字越小优先级越高

5.3 效果


Untitled


6 总结


世界之大,无奇不有。不得不说的是,伴随着AI的逐渐发展,类似这种项目未来一定是优先接入AI者才可以继续发展。


友情提示,喊fuck的时候先设置后双击control打开听写功能,喊完再点击一下control完成输入。


Untitled





作者:源心锁
来源:juejin.cn/post/7213651072145244221
收起阅读 »

30个Python操作小技巧

1、列表推导列表的元素可以在一行中进行方便的循环。numbers = [1, 2, 3, 4, 5, 6, 7, 8]even_numbers = [number for number in numbers if number % 2 == 0]print(e...
继续阅读 »

1、列表推导

列表的元素可以在一行中进行方便的循环。

numbers = [1, 2, 3, 4, 5, 6, 7, 8]
even_numbers = [number for number in numbers if number % 2 == 0]
print(even_numbers)

输出:

 [1,3,5,7]

同时,也可以用在字典上。

dictionary = {'first_num': 1, 'second_num': 2,
             'third_num': 3, 'fourth_num': 4}
oddvalues = {key: value for (key, value) in dictionary.items() if value % 2 != 0}
print(oddvalues)Output: {'first_num': 1, 'third_num': 3}

2、枚举函数

枚举是一个有用的函数,用于迭代对象,如列表、字典或文件。该函数生成一个元组,其中包括通过对象迭代获得的值以及循环计数器(从0的起始位置)。当您希望根据索引编写代码时,循环计数器很方便。

sentence = 'Just do It'
length = len(sentence)
for index, element in enumerate(sentence):
   print('{}: {}'.format(index, element))
    if index == 0:
       print('The first element!')
   elif index == length - 1:
       print('The last element!')

3、通过函数返回多个值

在设计函数时,我们经常希望返回多个值。这里我们将介绍两种典型的方法:

方法一

最简单的方式就是返回一个tuple。

get_student 函数,它根据员工的ID号以元组形式返回员工的名字和姓氏。

# returning a tuple.
def get_student(id_num):
   if id_num == 0:
       return 'Taha', 'Nate'
   elif id_num == 1:
       return 'Jakub', 'Abdal'
   else:
       raise Exception('No Student with this id: {}'.format(id_num))

Student = get_student(0)
print('first_name: {}, last_name: {}'.format(Student[0], Student[1]))

方法二、

返回一个字典类型。因为字典是键、值对,我们可以命名返回的值,这比元组更直观。

# returning a dictionary
def get_data(id_num):
   if id_num == 0:
       return {'first_name': 'Muhammad', 'last_name': 'Taha', 'title': 'Data Scientist', 'department': 'A', 'date_joined': '20200807'}
   elif id_num == 1:
       return {'first_name': 'Ryan', 'last_name': 'Gosling', 'title': 'Data Engineer', 'department': 'B', 'date_joined': '20200809'}
   else:
       raise Exception('No employee with this id: {}'.format(id_num))
employee = get_data(0)
print('first_name: {},nlast_name: {},ntitle: {},ndepartment: {},ndate_joined: {}'.format(
   employee['first_name'], employee['last_name'], employee['title'], employee['department'], employee['date_joined']))

4、像数学一样比较多个数字

如果你有一个值,并希望将其与其他两个值进行比较,则可以使用以下基本数学表达式:1<x<30。

你也许经常使用的是这种

1<x and x<30

在python中,你可以这么使用

x = 5
print(1<x<30)

5、将字符串转换为字符串列表:

当你输入 "[[1, 2, 3],[4, 5, 6]]" 时,你想转换为列表,你可以这么做。

import ast
def string_to_list(string):
return ast.literal_eval(string)
string = "[[1, 2, 3],[4, 5, 6]]"
my_list = string_to_list(string)
print(my_list)

6、对于Else方法

Python 中 esle 特殊的用法。

number_List = [1, 3, 8, 9,1]

for number in number_List:
if number % 2 == 0:
print(number)
break
else:
print("No even numbers!!")

7、在列表中查找n个最大或n个最小的元素

使用 heapq 模块在列表中查找n个最大或n个最小的元素。

import heapq
numbers = [80, 25, 68, 77, 95, 88, 30, 55, 40, 50]
print(heapq.nlargest(5, numbers))
print(heapq.nsmallest(5, numbers))

8、在不循环的情况下重复整个字符串

value = "Taha"
print(value * 5)
print("-" * 21)

9、从列表中查找元素的索引

cities= ['Vienna', 'Amsterdam', 'Paris', 'Berlin']
print(cities.index('Berlin'))

10、在同一行中打印多个元素?

print("Analytics", end="")
print("Vidhya")
print("Analytics", end=" ")
print("Vidhya")
print('Data', 'science', 'blogathon', '12', sep=', ')

输出

AnalyticsVidhya
Analytics Vidhya
Data, science, blogathon, 12

11、把大数字分开以便于阅读

有时,当你试图打印一个大数字时,传递整数真的很混乱,而且很难阅读。然后可以使用下划线,使其易于阅读。

print(5_000_000_000_000)

print(7_543_291_635)

输出:

5000000000000
7543291635

12、反转列表的切片

切片列表时,需要传递最小、最大和步长。要以相反的顺序进行切片,只需传递负步长。让我们来看一个例子:

sentence = "Data science blogathon"
print(sentence[21:0:-1])

输出

nohtagolb ecneics ata

13、 “is” 和 “==” 的区别。

如果要检查两个变量是否指向同一个对象,则需要使用“is”

但是,如果要检查两个变量是否相同,则需要使用“==”。

list1 = [7, 9, 4]
list2 = [7, 9, 4]
print(list1 == list2)
print(list1 is list2)
list3 = list1
print(list3 is list1)

输出

True
False
True

14、在一行代码中合并两个词典。

first_dct = {"London": 1, "Paris": 2}
second_dct = {"Tokyo": 3, "Seol": 4}
merged = {**first_dct, **second_dct}
print(merged)

输出

{‘London’: 1, ‘Paris’: 2, ‘Tokyo’: 3, ‘Seol’: 4}

15、识别字符串是否以特定字母开头

sentence = "Analytics Vidhya"
print(sentence.startswith("b"))
print(sentence.startswith("A"))

16、获得字符的Unicode

print(ord("T"))
print(ord("A"))
print(ord("h"))
print(ord("a"))

17、获取字典的键值对

cities = {'London': 1, 'Paris': 2, 'Tokyo': 3, 'Seol': 4}
for key, value in cities.items():
print(f"Key: {key} and Value: {value}")

18、在列表的特定位置添加值

cities = ["London", "Vienna", "Rome"]
cities.append("Seoul")
print("After append:", cities)
cities.insert(0, "Berlin")
print("After insert:", cities)

输出:

[‘London’, ‘Vienna’, ‘Rome’, ‘Seoul’] After insert: [‘Berlin’, ‘London’, ‘Vienna’, ‘Rome’, ‘Seoul’]

19、Filter() 函数

它通过在其中传递的特定函数过滤特定迭代器,并且返回一个迭代器。

mixed_number = [8, 15, 25, 30,34,67,90,5,12]
filtered_value = filter(lambda x: x > 20, mixed_number)
print(f"Before filter: {mixed_number}")
print(f"After filter: {list(filtered_value)}")

输出:

Before filter: [8, 15, 25, 30, 34, 67, 90, 5, 12]
After filter: [25, 30, 34, 67, 90]

20、创建一个没有参数个数限制的函数

def multiplication(*arguments):
mul = 1
for i in arguments:
mul = mul * i
return mul
print(multiplication(3, 4, 5))
print(multiplication(5, 8, 10, 3))
print(multiplication(8, 6, 15, 20, 5))

输出:

60
1200
72000

21、一次迭代两个或多个列表

capital = ['Vienna', 'Paris', 'Seoul',"Rome"]
countries = ['Austria', 'France', 'South Korea',"Italy"]
for cap, country in zip(capital, countries):
print(f"{cap} is the capital of {country}")

22、检查对象使用的内存大小

import sys
mul = 5*6
print(sys.getsizeof(mul))

23、 Map() 函数

map() 函数用于将特定函数应用于给定迭代器。

values_list = [8, 10, 6, 50]
quotient = map(lambda x: x/2, values_list)
print(f"Before division: {values_list}")
print(f"After division: {list(quotient)}")

24、计算 item 在列表中出现的次数

可以在 list 上调用 count 函数。

cities= ["Amsterdam", "Berlin", "New York", "Seoul", "Tokyo", "Paris", "Paris","Vienna","Paris"]
print("Paris appears", cities.count("Paris"), "times in the list")

25、在元组或列表中查找元素的索引

cities_tuple = ("Berlin", "Paris", 5, "Vienna", 10)
print(cities_tuple.index("Paris"))
cities_list = ['Vienna', 'Paris', 'Seoul',"Amsterdam"]
print(cities_list.index("Amsterdam"))

26、2个 set 进行 join 操作

set1 = {'Vienna', 'Paris', 'Seoul'}
set2 = {"Tokyo", "Rome",'Amsterdam'}
print(set1.union(set2))

27、根据频率对列表的值进行排序

from collections import Counter
count = Counter([7, 6, 5, 6, 8, 6, 6, 6])
print(count)
print("Sort values according their frequency:", count.most_common())

输出:

Counter({6: 5, 7: 1, 5: 1, 8: 1})
Sort values according their frequency: [(6, 5), (7, 1), (5, 1), (8, 1)]

28、从列表中删除重复值

cities_list = ['Vienna', 'Paris', 'Seoul',"Amsterdam","Paris","Amsterdam","Paris"]
cities_list = set(cities_list)
print("After removing the duplicate values from the list:",list(cities_list))

29、找出两个列表之间的差异

cities_list1 = ['Vienna', 'Paris', 'Seoul',"Amsterdam", "Berlin", "London"]
cities_list2 = ['Vienna', 'Paris', 'Seoul',"Amsterdam"]
cities_set1 = set(cities_list1)
cities_set2 = set(cities_list2)
difference = list(cities_set1.symmetric_difference(cities_set2))
print(difference)

30、将两个不同的列表转换为一个字典

number = [1, 2, 3]
cities = ['Vienna', 'Paris', 'Seoul']
result = dict(zip(number, cities))
print(result)

作者:程序员学长
来源:juejin.cn/post/7126728825274105886

收起阅读 »

Python办公软件自动化,5分钟掌握openpyxl操作

今天给大家分享一篇用openpyxl操作Excel的文章。各种数据需要导入Excel?多个Excel要合并?目前,Python处理Excel文件有很多库,openpyxl算是其中功能和性能做的比较好的一个。接下来我将为大家介绍各种Excel操作。打开Excel...
继续阅读 »

今天给大家分享一篇用openpyxl操作Excel的文章。

各种数据需要导入Excel?多个Excel要合并?目前,Python处理Excel文件有很多库,openpyxl算是其中功能和性能做的比较好的一个。接下来我将为大家介绍各种Excel操作。

打开Excel文件

新建一个Excel文件


打开现有Excel文件


打开大文件时,根据需求使用只读或只写模式减少内存消耗。


获取、创建工作表

获取当前活动工作表:


创建新的工作表:


使用工作表名字获取工作表:


获取所有的工作表名称:


保存

保存到流中在网络中使用:



单元格
单元格位置作为工作表的键直接读取:


为单元格赋值:


多个单元格 可以使用切片访问单元格区域:


使用数值格式:


使用公式:


合并单元格时,除左上角单元格外,所有单元格都将从工作表中删除:


行、列
可以单独指定行、列、或者行列的范围:


可以使用Worksheet.iter_rows()方法遍历行:


同样的Worksheet.iter_cols()方法将遍历列:


遍历文件的所有行或列,可以使用Worksheet.rows属性:


Worksheet.columns属性:


使用Worksheet.append()或者迭代使用Worksheet.cell()新增一行数据:


插入操作比较麻烦。可以使用Worksheet.insert_rows()插入一行或几行:


Worksheet.insert_cols()操作类似。Worksheet.delete_rows()Worksheet.delete_cols()用来批量删除行和列。

只读取值
使用Worksheet.values属性遍历工作表中的所有行,但只返回单元格值:


Worksheet.iter_rows()Worksheet.iter_cols()可以设置values_only参数来仅返回单元格的值:



作者:Sinchard | 来源:python中文社区

收起阅读 »

Py大蟒蛇搞机器人

itchat使用教程itchat是一个开源的微信个人号接口,使用python调用微信从未如此简单。使用不到三十行的代码,你就可以完成一个能够处理所有信息的微信机器人。首先,在终端安装一下itchat:#pip是pyth的包管理工具也就是pyth的应用商店专门用...
继续阅读 »

itchat使用教程

itchat是一个开源的微信个人号接口,使用python调用微信从未如此简单。使用不到三十行的代码,你就可以完成一个能够处理所有信息的微信机器人。

首先,在终端安装一下itchat:

#pip是pyth的包管理工具也就是pyth的应用商店专门用来安装和卸载库
pip install itchat

1.登录

  1. login() - 每次运行程序都需要扫二维码

  2. login(hotReload==True) - 下次登录不用再扫二维码

  3. auto_login(loginCallback=登录成功回调函数, exitCallback=退出登录回调函数)

2.退出登录

  1. logout() - 强制退出登录

3.获取好友信息

  1. get_friends(update=True) - 获取所有的好友信息

  2. get_chatrooms() - 获取群组

  3. get_mps() - 获取公众号

  4. get_msg() - 获取消息列表

  5. get_head_img() - 获取个人头像

4.发送消息

send(msg=消息内容, toUserName=用户名)

1).msg的值会因为消息类型不同而不同:

  • 文本消息 - 引号中直接写要发送的文字内容

  • 发送文件 - @fil@文件路径

  • 发送图片 - @img@图片路径

  • 发送视频 - @vid@视频路径

2).toUserName: 发送对象,如果不填就发送给自己

5.接收消息

想要自动接收消息,需要先对不同类型的消息进行注册,如果没有注册,对应类型的消息将不会被接收.

注册的方式如下:

@itchat.msg_register(消息类型,isFriendChat=True, isGroupChat=True,isMpChat=True)

def 函数名(msg):
#接收到对应的消息会自动执行的代码段
  #msg.download(msg['FileName'])   #这个同样是下载文件的方式
  #msg['Text'](msg['FileName'])     #下载文件

1)消息类型:

参数类型Text键值
TEXT文本文本内容(文字消息)
CARD名片推荐人字典(推荐人的名片)
SHARING分享分享名称(分享的音乐或者文章等)
RECORDING语音下载方法
ATTACHMENT附件下载方法
VIDEO小视频下载方法
FRIENDS好友邀请添加好友所需参数
SYSTEM系统消息更新内容的用户或群聊的UserName组成的列表
MAP地图位置文本(位置分享)
NOTE通知通知文本(消息撤回等)
PICTURE图片/表情下载方法

来源:blog.csdn.net/weixin_46014553/article/details/110200748

收起阅读 »

Python-可变和不可变类型

1. 不可变类型不可变类型,内存中的数据不允许被修改(一旦被定义,内存中分配了小格子,就不能再修改内容了):数字类型int,bool,float,complex,long(2,x)字符串str元组tuple2. 可变类型可变类型,内存中的数据可以被修改(可以通...
继续阅读 »

1. 不可变类型

不可变类型,内存中的数据不允许被修改(一旦被定义,内存中分配了小格子,就不能再修改内容了):

  • 数字类型intboolfloatcomplexlong(2,x)

  • 字符串str

  • 元组tuple

2. 可变类型

可变类型,内存中的数据可以被修改(可以通过变量名调用方法来修改列表和字典内部的内容,而内存地址不发生变化):

  • 列表list

  • 字典dict(注:字典中的key只能使用不可变类型的数据)

注:给变量赋新值的时候,只是改变了变量的引用地址,不是修改之前的内容

  1. 可变类型的数据变化,是通过方法来实现的

  2. 如果给一个可变类型的变量,复制了一个新的数据,引用会修改(变量从之前的数据上撕下来,贴到新赋值的数据上)

3. 代码演示

# 新建列表
a = [1, 2, 3]
print("列表a:", a)
print("列表a的地址:", id(a))
print("*"*50)
# 追加元素
a.append(999)
print("列表a:", a)
print("列表a的地址:", id(a))
print("*"*50)
# 移除元素
a.remove(2)
print("列表a:", a)
print("列表a的地址:", id(a))
print("*"*50)
# 清空列表
a.clear()
print("列表a:", a)
print("列表a的地址:", id(a))
print("*"*50)
# 将空列表赋值给变量a
a = []
print("列表a的地址:", id(a))   # 通过输出可以看出地址发生了变化
print("*"*50)
# 新建字典
d = {"name": "xiaoming"}
print("字典d为:", d)
print("字典d的地址:", id(d))
print("*"*50)
# 追加键值对
d["age"] = 18
print("字典d为:", d)
print("字典d的地址:", id(d))
print("*"*50)
# 删除键值对
d.pop("age")
print("字典d为:", d)
print("字典d的地址:", id(d))
print("*"*50)
# 清空所有键值对
d.clear()
print("字典d为:", d)
print("字典d的地址:", id(d))
print("*"*50)
# 对d赋值空字典
d = {}
print("字典d为:", d)
print("字典d的地址:", id(d))
print("*"*50)

4. 运行结果

可变类型(列表和字典)的数据变化,是通过方法(比如append,remove,pop等)来实现的,不会改变地址。而重新赋值后地址会改变。具体运行结果如下图所示:




作者:ZacheryZHANG
来源:juejin.cn/post/7100423532655411213

收起阅读 »

2022年最值得收藏的 25 个 Python 文本处理案例!

目录1提取PDF内容2提取Word内容3提取Web网页内容4读取json数据5读取CSV数据6删除字符串中的标点符号7使用NLTK删除停用词8使用TextBlob更正拼写9使用NLTK和TextBlob的词标记化10使用NLTK提取句子单词或短语的词干列表11...
继续阅读 »

1提取 PDF 内容

  1. # pip install PyPDF2 安装 PyPDF2
  2. import PyPDF2
  3. from PyPDF2 import PdfFileReader
  4.  
  5. # Creating a pdf file object.
  6. pdf = open("test.pdf", "rb")
  7.  
  8. # Creating pdf reader object.
  9. pdf_reader = PyPDF2.PdfFileReader(pdf)
  10.  
  11. # Checking total number of pages in a pdf file.
  12. print("Total number of Pages:", pdf_reader.numPages)
  13.  
  14. # Creating a page object.
  15. page = pdf_reader.getPage(200)
  16.  
  17. # Extract data from a specific page number.
  18. print(page.extractText())
  19.  
  20. # Closing the object.
  21. pdf.close()

2提取 Word 内容

  1. # pip install python-docx 安装 python-docx
  2.  
  3.  
  4. import docx
  5.  
  6.  
  7. def main():
  8.      try:
  9.      doc = docx.Document('test.docx') # Creating word reader object.
  10.      data = ""
  11.      fullText = []
  12.      for para in doc.paragraphs:
  13.          fullText.append(para.text)
  14.          data = '\n'.join(fullText)
  15.  
  16.      print(data)
  17.  
  18.      except IOError:
  19.      print('There was an error opening the file!')
  20.      return
  21.  
  22.  
  23. if __name__ == '__main__':
  24.      main()

3提取 Web 网页内容

  1. # pip install bs4 安装 bs4
  2.  
  3. from urllib.request import Request, urlopen
  4. from bs4 import BeautifulSoup
  5.  
  6. req = Request('http://www.cmegroup.com/trading/products/#sortField=oi&sortAsc=false&venues=3&page=1&cleared=1&group=1',
  7.          headers={'User-Agent': 'Mozilla/5.0'})
  8.  
  9. webpage = urlopen(req).read()
  10.  
  11. # Parsing
  12. soup = BeautifulSoup(webpage, 'html.parser')
  13.  
  14. # Formating the parsed html file
  15. strhtm = soup.prettify()
  16.  
  17. # Print first 500 lines
  18. print(strhtm[:500])
  19.  
  20. # Extract meta tag value
  21. print(soup.title.string)
  22. print(soup.find('meta', attrs={'property':'og:description'}))
  23.  
  24. # Extract anchor tag value
  25. for x in soup.find_all('a'):
  26.      print(x.string)
  27.  
  28. # Extract Paragraph tag value
  29. for x in soup.find_all('p'):
  30.      print(x.text)

4读取 Json 数据

  1. import requests
  2. import json
  3.  
  4. = requests.get("https://support.oneskyapp.com/hc/en-us/article_attachments/202761727/example_2.json")
  5. res = r.json()
  6.  
  7. # Extract specific node content.
  8. print(res['quiz']['sport'])
  9.  
  10. # Dump data as string
  11. data = json.dumps(res)
  12. print(data)

5读取 CSV 数据

  1. import csv
  2.  
  3. with open('test.csv','r') as csv_file:
  4.      reader =csv.reader(csv_file)
  5.      next(reader) # Skip first row
  6.      for row in reader:
  7.      print(row)

6删除字符串中的标点符号

  1. import re
  2. import string
  3.  
  4. data = "Stuning even for the non-gamer: This sound track was beautiful!\
  5. It paints the senery in your mind so well I would recomend\
  6. it even to people who hate vid. game music! I have played the game Chrono \
  7. Cross but out of all of the games I have ever played it has the best music! \
  8. It backs away from crude keyboarding and takes a fresher step with grate\
  9. guitars and soulful orchestras.\
  10. It would impress anyone who cares to listen!"
  11.  
  12. # Methood 1 : Regex
  13. # Remove the special charaters from the read string.
  14. no_specials_string = re.sub('[!#?,.:";]', '', data)
  15. print(no_specials_string)
  16.  
  17.  
  18. # Methood 2 : translate()
  19. # Rake translator object
  20. translator = str.maketrans('', '', string.punctuation)
  21. data = data.translate(translator)
  22. print(data)

7使用 NLTK 删除停用词

  1. from nltk.corpus import stopwords
  2.  
  3.  
  4. data = ['Stuning even for the non-gamer: This sound track was beautiful!\
  5. It paints the senery in your mind so well I would recomend\
  6. it even to people who hate vid. game music! I have played the game Chrono \
  7. Cross but out of all of the games I have ever played it has the best music! \
  8. It backs away from crude keyboarding and takes a fresher step with grate\
  9. guitars and soulful orchestras.\
  10. It would impress anyone who cares to listen!']
  11.  
  12. # Remove stop words
  13. stopwords = set(stopwords.words('english'))
  14.  
  15. output = []
  16. for sentence in data:
  17.      temp_list = []
  18.      for word in sentence.split():
  19.      if word.lower() not in stopwords:
  20.          temp_list.append(word)
  21.      output.append(' '.join(temp_list))
  22.  
  23.  
  24. print(output)

8使用 TextBlob 更正拼写

  1. from textblob import TextBlob
  2.  
  3. data = "Natural language is a cantral part of our day to day life, and it's so antresting to work on any problem related to langages."
  4.  
  5. output = TextBlob(data).correct()
  6. print(output)

9使用 NLTK 和 TextBlob 的词标记化

  1. import nltk
  2. from textblob import TextBlob
  3.  
  4.  
  5. data = "Natural language is a central part of our day to day life, and it's so interesting to work on any problem related to languages."
  6.  
  7. nltk_output = nltk.word_tokenize(data)
  8. textblob_output = TextBlob(data).words
  9.  
  10. print(nltk_output)
  11. print(textblob_output)

Output:

['Natural', 'language', 'is', 'a', 'central', 'part', 'of', 'our', 'day', 'to', 'day', 'life', ',', 'and', 'it', "'s", 'so', 'interesting', 'to', 'work', 'on', 'any', 'problem', 'related', 'to', 'languages', '.']
['Natural', 'language', 'is', 'a', 'central', 'part', 'of', 'our', 'day', 'to', 'day', 'life', 'and', 'it', "'s", 'so', 'interesting', 'to', 'work', 'on', 'any', 'problem', 'related', 'to', 'languages']

10使用 NLTK 提取句子单词或短语的词干列表

  1. from nltk.stem import PorterStemmer
  2.  
  3. st = PorterStemmer()
  4. text = ['Where did he learn to dance like that?',
  5.      'His eyes were dancing with humor.',
  6.      'She shook her head and danced away',
  7.      'Alex was an excellent dancer.']
  8.  
  9. output = []
  10. for sentence in text:
  11.      output.append(" ".join([st.stem(i) for i in sentence.split()]))
  12.  
  13. for item in output:
  14.      print(item)
  15.  
  16. print("-" * 50)
  17. print(st.stem('jumping'), st.stem('jumps'), st.stem('jumped'))

Output:

where did he learn to danc like that?
hi eye were danc with humor.
she shook her head and danc away
alex wa an excel dancer.
--------------------------------------------------
jump jump jump

11使用 NLTK 进行句子或短语词形还原

  1. from nltk.stem import WordNetLemmatizer
  2.  
  3. wnl = WordNetLemmatizer()
  4. text = ['She gripped the armrest as he passed two cars at a time.',
  5.      'Her car was in full view.',
  6.      'A number of cars carried out of state license plates.']
  7.  
  8. output = []
  9. for sentence in text:
  10.      output.append(" ".join([wnl.lemmatize(i) for i in sentence.split()]))
  11.  
  12. for item in output:
  13.      print(item)
  14.  
  15. print("*" * 10)
  16. print(wnl.lemmatize('jumps', 'n'))
  17. print(wnl.lemmatize('jumping', 'v'))
  18. print(wnl.lemmatize('jumped', 'v'))
  19.  
  20. print("*" * 10)
  21. print(wnl.lemmatize('saddest', 'a'))
  22. print(wnl.lemmatize('happiest', 'a'))
  23. print(wnl.lemmatize('easiest', 'a'))

Output:

She gripped the armrest a he passed two car at a time.
Her car wa in full view.
A number of car carried out of state license plates.
**********
jump
jump
jump
**********
sad
happy
easy

12使用 NLTK 从文本文件中查找每个单词的频率

  1. import nltk
  2. from nltk.corpus import webtext
  3. from nltk.probability import FreqDist
  4.  
  5. nltk.download('webtext')
  6. wt_words = webtext.words('testing.txt')
  7. data_analysis = nltk.FreqDist(wt_words)
  8.  
  9. # Let's take the specific words only if their frequency is greater than 3.
  10. filter_words = dict([(m, n) for m, n in data_analysis.items