当前位置: 首页 > news >正文

Flask新手教程

Flask简介

Flask是一个轻量级的可定制框架,使用Python语言编写,较其他同类型框架更为灵活、轻便、安全且容易上手。

Flask 可以很好地结合MVC模式进行开发,开发人员分工合作,小型团队在短时间内就可以完成功能丰富的中小型网站或Web服务的实现。

Flask还有很强的定制性,用户可以根据自己的需求来添加相应的功能,在保持核心功能简单的同时实现功能的丰富与扩展,其强大的插件库可以让用户实现个性化的网站定制,开发出功能强大的网站。

Flask主要特征

Flask是目前十分流行的web框架。

Flask 被称为微框架(microframework),“微”并不是意味着把整个Web应用放入到一个Python文件,微框架中的“微”是指Flask旨在保持代码简洁且易于扩展。

Flask框架的主要特征是核心构成比较简单,但具有很强的扩展性和兼容性,程序员可以使用Python语言快速实现一个网站或Web服务。

Flask两个核心函数库

Flask主要包括Werkzeug和Jinja2两个核心函数库,它们分别负责业务处理和安全方面的功能,这些基础函数为web项目开发过程提供了丰富的基础组件。

Werkzeug

Werkzeug库十分强大,功能比较完善,支持URL路由请求集成,一次可以响应多个用户的访问请求;

支持Cookie和会话管理,通过身份缓存数据建立长久连接关系,并提高用户访问速度;支持交互式Javascript调试,提高用户体验;

可以处理HTTP基本事务,快速响应客户端推送过来的访问请求。

Jinja2

Jinja2库支持自动HTML转移功能,能够很好控制外部黑客的脚本攻击。系统运行速度很快,页面加载过程会将源码进行编译形成python字节码,从而实现模板的高效运行;

模板继承机制可以对模板内容进行修改和维护,为不同需求的用户提供相应的模板。

Flask 基本模式

Flask的基本模式是在程序里将一个视图函数分配给一个URL,每当用户访问这个URL时,系统就会执行给该URL分配好的视图函数,获取函数的返回值并将其显示到浏览器上,工作过程见图。

Flask 安装

 安装

通过pip安装Flask即可:

pip install Flask

如果你的命令是pip3, 则使用 pip3 install Flask 

进入python交互模式看下Flask的介绍:

我们可以看到Flask使用 BSD 授权。

查看一下flask版本:

我们使用的Flask版本号是 1.1.1

Flask 目录结构

我们通过别人的目录大致了解一下flask框架的目录结构。

flask-demo/├ run.py           # 应用启动程序├ config.py        # 环境配置├ requirements.txt # 列出应用程序依赖的所有Python包├ tests/           # 测试代码包│   ├ __init__.py │   └ test_*.py    # 测试用例└ myapp/├ admin/       # 蓝图目录├ static/│   ├ css/     # css文件目录│   ├ img/     # 图片文件目录│   └ js/      # js文件目录├ templates/   # 模板文件目录├ __init__.py    ├ forms.py     # 存放所有表单,如果多,将其变为一个包├ models.py    # 存放所有数据模型,如果多,将其变为一个包└ views.py     # 存放所有视图函数,如果多,将其变为一个包

当然我们不需要创建这么多文件和文件夹也能运行Flask, 下面的课程会介绍到。

Hello World 开始

本节主要内容:使用Flask写一个显示 "Hello World!"的web程序,如何配置、调试Flask。

创建项目

我们手动创建文件夹和文件结构如下:

py/├ static/ # static用来存放静态资源,例如图片、js、css文件等├ templates/ # templates存放模板文件├ server.py

我们的网站逻辑基本在server.py文件中,当然,也可以给这个文件起个其他的名字。

server.py中加入以下内容:

from flask import Flaskapp = Flask(__name__)
@app.route('/')
def hello_world():return 'Hello World!'if __name__ == '__main__':app.run()

 运行server.py

python3 server.py * Running on http://127.0.0.1:5000/

打开浏览器访问http://127.0.0.1:5000/

代码解析

变量app是一个Flask实例,通过下面的方式:

@app.route('/')
def hello_world():return 'Hello World!'

当客户端访问/时,将响应hello_world()函数返回的内容。注意,这不是返回Hello World!这么简单,Hello World!只是HTTP响应报文的实体部分,状态码等信息既可以由Flask自动处理,当然也可以通过编程来制定。

修改Flask的配置

app = Flask(__name__)

上面的代码中,python内置变量__name__的值是字符串__main__ 。Flask类将这个参数作为程序名称。当然这个是可以自定义的,比如:

app = Flask("myapp")

Flask默认使用static目录存放静态资源,templates目录存放模板,也可以通过设置参数更改:

app = Flask("myapp", static_folder="path1", template_folder="path2")

以上我们指定静态资源目录为path1, 模板目录为 path2。

更多参数请参考__doc__

from flask import Flask
print(Flask.__doc__)

调试模式

上面的server.py中以app.run()方式运行,这种方式下,如果服务器端出现错误是不会在客户端显示的。但是在开发环境中,显示错误信息是很有必要的,要显示错误信息,应该以下面的方式运行Flask:

app.run(debug=True)

debug设置为True的另一个好处是,程序启动后,会自动检测源码是否发生变化,若有变化则自动重启程序。这可以帮我们省下很多时间。

还可以使用这种途径开启调试模式:

app.debug = True
app.run()

绑定IP和端口

默认情况下,Flask绑定IP为127.0.0.1,端口为5000。也可以通过下面的方式自定义:

app.run(host='0.0.0.0', port=8080, debug=True)

0.0.0.0代表电脑所有的IP。以上我们绑定了8080端口, 启动服务后我们访问的网址将是: http://127.0.0.1:8080/

列出所有的url参数

在server.py中添加以下内容:

from flask import Flask, requestapp = Flask(__name__)@app.route('/')
def hello_world():return request.args.__str__()if __name__ == '__main__':app.run(port=5000, debug=True)

在浏览器中访问http://127.0.0.1:5000/?name=Loen&age&app=ios&app=android,将显示:

ImmutableMultiDict([('name', 'Loen'), ('age', ''), ('app', 'ios'), ('app', 'android')])

中文

较新的浏览器也支持直接在url中输入中文(最新的火狐浏览器内部会帮忙将中文转换成符合URL规范的数据),在浏览器中访问http://127.0.0.1:5000/?info=我爱你,将显示:

ImmutableMultiDict([('info', '我爱你')])

request.full_path和request.path

可以通过request.full_pathrequest.path 查看浏览器传给我们的Flask服务的数据

from flask import Flask, requestapp = Flask(__name__)@app.route('/')
def hello_world():print(request.path) # 这里的 print 会在控制台中输出print(request.full_path)return request.args.__str__()if __name__ == '__main__':app.run(port=5000, debug=True)

浏览器访问http://127.0.0.1:5000/?info=我爱你&to=girl,运行server.py的终端会输出:

 获取指定的参数值

例如,要获取键info对应的值,如下修改server.py

from flask import Flask, requestapp = Flask(__name__)@app.route('/')
def hello_world():return request.args.get('info')if __name__ == '__main__':app.run(port=5000)

运行server.py,在浏览器中访问http://127.0.0.1:5000/?info=我爱你&to=girl,浏览器将显示:

开启debug

不过,当我们访问http://127.0.0.1:5000/时候却出现了500错误,浏览器显示:

我们返回server.py, 开启debug

再次访问:

这是因为Flask不允许返回None, 而没有在URL参数中找到info。所以request.args.get('info') 返回Python内置的None。

解决方法很简单,我们先判断下它是不是None:

from flask import Flask, requestapp = Flask(__name__)@app.route('/')
def hello_world():r = request.args.get('info')if r==None:# do somethingreturn ''return rif __name__ == '__main__':app.run(port=5000, debug=True)

也可以, 设置默认值,也就是取不到数据时用这个值:

from flask import Flask, requestapp = Flask(__name__)@app.route('/')
def hello_world():r = request.args.get('info', 'welcome')return rif __name__ == '__main__':app.run(port=5000, debug=True)

函数request.args.get的第二个参数用来设置默认值。此时在浏览器访问http://127.0.0.1:5000/,将显示:

welcome

getlist 如何处理多值

如果我们请求 http://127.0.0.1:5000/?name=Loen&age&app=ios&app=android,仔细看下,app有两个值。

如果我们的代码是:

from flask import Flask, requestapp = Flask(__name__)@app.route('/')
def hello_world():r = request.args.get('app')return rif __name__ == '__main__':app.run(port=5000, debug=True)

在浏览器中请求时,我们只会看到 ios

我们可以使用getlist获取所有的app的值

from flask import Flask, requestapp = Flask(__name__)@app.route('/')
def hello_world():r = request.args.getlist('app')  # 返回一个listreturn str(r)if __name__ == '__main__':app.run(port=5000, debug=True)

浏览器输入 http://127.0.0.1:5000/?name=Loen&age&app=ios&app=android,我们会看到['ios', 'android']

获取POST方法传送的数据

作为一种HTTP请求方法,POST用于向指定的资源提交要被处理的数据。

比如: 我们在某网站注册用户、写文章等时候,需要将数据传递到网站服务器中。并不适合将数据放到URL参数中,密码放到URL参数中容易被看到,文章数据又太多,浏览器不一定支持太长长度的URL。这时,一般使用POST方法。

本课程使用python的requests库模拟浏览器。

安装方法:

pip install requests

看POST数据内容

以用户注册为例子,我们需要向服务器/register传送用户名name和密码password。如下编写server.py。

from flask import Flask, requestapp = Flask(__name__)@app.route('/register', methods=['POST'])
def register():print(request.headers)print(request.stream.read())return 'welcome'if __name__ == '__main__':app.run(port=5000, debug=True)

@app.route('/register', methods=['POST'])​是指url​/register​只接受POST方法。可以根据需要修改methods参数,例如如果想要让它同时支持GET和POST,这样写:

@app.route('/register', methods=['GET', 'POST']) 

浏览器模拟工具client.py内容如下:

import requestsuser_info = {'name': 'Loen', 'password': 'loveyou'}
r = requests.post("http://127.0.0.1:5000/register", data=user_info)print(r.text)

运行server.py,然后运行client.pyclient.py将输出:

welcome

而server.py在终端中输出以下调试信息(通过print输出):

前6行是client.py生成的HTTP请求头,由print(request.headers)输出。

请求体的数据,我们通过print(request.stream.read())输出,结果是:

b'name=Loen&password=loveyou'

解析POST数据

上面,我们看到post的数据内容是:

b'name=Loen&password=loveyou'

我们要想办法把我们要的name、password提取出来,怎么做呢?

Flask已经内置了解析器request.form。

我们将服务代码改成:

from flask import Flask, requestapp = Flask(__name__)@app.route('/register', methods=['POST'])
def register():print(request.headers)# print(request.stream.read()) # 不要用,否则下面的form取不到数据print(request.form)print(request.form['name'])print(request.form.get('name'))print(request.form.getlist('name'))print(request.form.get('nickname', default='little apple'))return 'welcome'if __name__ == '__main__':app.run(port=5000, debug=True)

执行client.py请求数据,服务器代码会在终端输出:

request.form会自动解析数据。

request.form['name']和request.form.get('name')都可以获取name对应的值。对于request.form.get()可以为参数default指定值以作为默认值。所以:

print(request.form.get('nickname', default='little apple'))

输出的是默认值

little apple

获取POST中的列表数据

如果name有多个值,可以使用request.form.getlist('name'),该方法将返回一个列表。我们将client.py改一下:

import requestsuser_info = {'name': ['Loen', 'Alan'], 'password': 'loveyou'}
r = requests.post("http://127.0.0.1:5000/register", data=user_info)print(r.text)

此时运行client.pyprint(request.form.getlist('name'))将输出:

[u'Loen', u'Alan']

处理和响应JSON数据

使用 HTTP POST 方法传到网站服务器的数据格式可以有很多种,比如「获取POST方法传送的数据」课程中讲到的name=Loen&password=loveyou这种用过&符号分割的key-value键值对格式。我们也可以用JSON格式、XML格式。相比XML的重量、规范繁琐,JSON显得非常小巧和易用。

如果POST的数据是JSON格式,request.json会自动将json数据转换成Python类型(字典或者列表)。

编写server.py:

from flask import Flask, requestapp = Flask("myapp")@app.route('/add', methods=['POST'])
def add():print(request.headers)print(type(request.json))print(request.json)result = request.json['n1'] + request.json['n2']return str(result)if __name__ == '__main__':app.run(host='127.0.0.1', port=5000, debug=True)

编写client.py模拟浏览器请求:

import requestsjson_data = {'n1': 5, 'n2': 3}r = requests.post("http://127.0.0.1:5000/add", json=json_data)print(r.text)

运行server.py,然后运行client.pyclient.py 会在终端输出:

注意,请求头中Content-Type的值是application/json

响应JSON

响应JSON时,除了要把响应体改成JSON格式,响应头的Content-Type也要设置为application/json。

编写server.py:

from flask import Flask, request, Response
import jsonapp = Flask("myapp")@app.route('/add', methods=['POST'])
def add():result = {'sum': request.json['n1'] + request.json['n2']}return Response(json.dumps(result),  mimetype='application/json')if __name__ == '__main__':app.run(host='127.0.0.1', port=5000, debug=True)

修改后运行。

编写client.py:

import requestsjson_data = {'n1': 5, 'n2': 3}r = requests.post("http://127.0.0.1:5000/add", json=json_data)print(r.headers)
print(r.text)

运行client.py,将显示:

client终端返回的第一段内容是服务器的响应头,第二段内容是响应体,也就是服务器返回的JSON格式数据。

另外,如果需要服务器的HTTP响应头具有更好的可定制性,比如自定义Server,可以如下修改add()函数:

@app.route('/add', methods=['POST'])
def add():result = {'sum': request.json['n1'] + request.json['n2']}resp = Response(json.dumps(result),  mimetype='application/json')resp.headers.add('Server', 'python flask')return resp

client.py运行后会输出:

{'Content-Type': 'application/json', 'Content-Length': '10', 'Server': 'python flask', 'Date': 'Wed, 11
Sep 2019 09:09:18 GMT'}
{"sum": 8}

响应JSON

使用 jsonify 工具函数。

from flask import Flask, request, jsonifyapp = Flask("myapp")@app.route('/add', methods=['POST'])
def add():result = {'sum': request.json['n1'] + request.json['n2']}return jsonify(result)if __name__ == '__main__':app.run(host='127.0.0.1', port=5000, debug=True)

运行结果:

 

 

相关文章:

Flask新手教程

Flask简介 Flask是一个轻量级的可定制框架,使用Python语言编写,较其他同类型框架更为灵活、轻便、安全且容易上手。 Flask 可以很好地结合MVC模式进行开发,开发人员分工合作,小型团队在短时间内就可以完成功能丰富的中小型网站或…...

拼多多API接口,百亿补贴商品详情页面采集

电商API的数据类型 电商API提供的数据种类多样,一般可分为以下几类: 1.商品数据:商品ID、商品名称、商品价格、库存等。 2.交易数据:订单号、付款时间、收货人等。 3.店铺数据:店铺ID、店铺名称、开店时间、店铺评…...

C++入门(未完待续)

1.命名空间 使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染 定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员 ①.普通的命名空间 n…...

Python爬虫学习笔记(四)————XPath解析

目录 0.xpath最新下载地址和安装教程 1.xpath安装 2.xpath基本使用 3.xpath基本语法 4.实例 (1)xpath解析本地文件 (2)xpath解析服务器响应的数据 ①获取百度网站的“百度一下”四个字 ②获取站长素材网站情侣图片前十页的…...

知识图谱推理的学习逻辑规则(上)

知识图谱推理的学习逻辑规则 摘要介绍相关工作模型知识图谱推理逻辑规则概率形式化参数化优化 实验实验设置实验结果 结论 原文: 摘要 本文研究了在知识图谱上进行推理的学习逻辑规则。 逻辑规则用于预测时提供了可解释性并且可以推广到其他任务中,因…...

【从零开始学习C++ | 第二十一篇】C++新增特性 (上)

目录 前言: 委托构造函数: 类内初始化: 空指针: 枚举类: 总结: 前言: C的学习难度大,内容繁多。因此我们要及时掌握C的各种特性,因此我们更新本篇文章,向…...

你真的会用async和await么?

背景 背景就是遇到了一个比较烦人的模块,里面的涉及到了大量的async 和 awiat。发现大多人对这个语法糖一知半解,然后大量的滥用,整理一下 async 前置知识: Promise.resolve(foo) new Promise(resolve > resolve(foo)…...

vscode远程连接提示:过程试图写入的管道不存在(删除C:\Users\<用户名>\.ssh\known_hosts然后重新连接)

文章目录 复现过程原因解决方法总结 复现过程 我是在windows上用vscode远程连接到我的ubuntu虚拟机上,后来我的虚拟机出了点问题,我把它回退了,然后再连接就出现了这个问题 原因 本地的known_hosts文件记录服务器信息与现服务器的信息冲突了…...

【005】基于深度学习的图像语 通信系统

摘要 语义通信是一种新颖的通信方式,可通过传输数据的语义信息提高带宽效率。提出一种用于无线图像传输的系统。该系统基于深度学习技术开发并以端到端(E2E)的方式进行训练。利用深度学习实现语义特征的提取和重建,在发送端提取信…...

基于ssm的社区生活超市的设计与实现

博主介绍:专注于Java技术领域和毕业项目实战。专注于计算机毕设开发、定制、文档编写指导等,对软件开发具有浓厚的兴趣,工作之余喜欢钻研技术,关注IT技术的发展趋势,感谢大家的关注与支持。 技术交流和部署相关看文章…...

长短期记忆网络(LSTM)原理解析

长短期记忆网络(Long Short-Term Memory,简称LSTM)是一种常用于处理序列数据的深度学习模型。它在循环神经网络(Recurrent Neural Network,RNN)的基础上进行了改进,旨在解决传统RNN中的梯度消失…...

vscode debug的方式

在.vscode文件夹下建立launch.json 例子1:调试python 来自 https://github.com/chunleili/tiPBD/tree/amg {"version": "0.2.0","configurations": [{"name": "hpbd 5 5","type": "python&quo…...

微信加粉计数器后台开发

后台包括管理后台与代理后台两部分 管理后台 管理后台自带网络验证卡密系统,一个后台可以完成对Pc端的全部对接,可以自定义修改分组名称 分享等等代理后台 分享页 调用示例 <?php$request new HttpRequest(); $request->setUrl(http://xxxxxxx/api); $request->…...

黑客是什么?想成为黑客需要学习什么?

什么是黑客 在《黑客辞典》里有不少关于“黑客”的定义, 大多和“精于技术”或“乐于解决问题并超越极限”之类的形容相关。然而&#xff0c;若你想知道如何成为一名黑客&#xff0c;只要牢记两点即可。 这是一个社区和一种共享文化&#xff0c;可追溯到那群数十年前使…...

iOS中__attribute__的使用

通过__attribute编译期指令将数据注册至Mach-O指定段的section&#xff0c;可以提供更灵活的注册方式&#xff0c;避免了非必要依赖。通过这种方式不仅仅能够在任何地方注册string&#xff0c;甚至可以注册C函数。 下面的库提供了注册和读取内容的简单方式&#xff0c;主要支持…...

腾讯、飞书等在线表格自动化编辑--python

编辑在线表格 一 目的二 实现效果三 实现过程简介1、本地操作表格之后进入导入在线文档2、直接操作在线文档 四 实现步骤讲解1、实现方法的选择2、导入类库3、设置浏览器代理直接操作已打开浏览器4、在线文档登录5、在线文档表格数据操作6、行数不够自动添加行数 五 代码实现小…...

开源库nlohmann json使用备忘

nlohmann/json是一个用于解析JSON的开源C库&#xff0c;口碑一流&#xff0c;无需额外安装其他第三方库&#xff0c;还支持单个头文件模式&#xff0c;使用起来非常方便直观。 1. 编译 从官网https://github.com/nlohmann/json的Release页面下载单个json.hpp即可直接使用&…...

语音识别开源框架 openAI-whisper

Whisper 是一种通用的语音识别模型。 它是OpenAI于2022年9月份开源的在各种音频的大型数据集上训练的语音识别模型&#xff0c;也是一个可以执行多语言语音识别、语音翻译和语言识别的多任务模型。 GitHub - yeyupiaoling/Whisper-Finetune: 微调Whisper语音识别模型和加速推理…...

php做的中秋博饼游戏之绘制骰子图案功能示例

先看代码 header(Content-Type:image/png); $img imagecreatetruecolor(200, 200); $white imagecolorallocate($img, 255, 255, 255); $grey imagecolorallocate($img, 100, 100, 100); $blue imagecolorallocate($img, 0, 102, 255); $red imagecolorallocate($img, …...

erlang 虚拟机优化参数

sbwt none 将CPU忙等待关闭将有助于降低系统显示的CPU使用率&#xff0c;因为开启了忙等待的BEAM&#xff0c;CPU负载并不代表真实的工作情况&#xff1b; K true 开启epoll IO模型 swt low Sets scheduler wakeup threshold. Defaults to medium. The thresh…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...