Python学习笔记(2)——变量类型和运算符

区别于C/C++, java等编程语言,python是一门弱类型语言,如果你接触过perl或者JS之类的弱类型语言,那么对这个特性应该不陌生。
当然即使是动态类型语言,变量也是有类型的,只是定义跟赋值上更加的自由,多数时候我都是把这个特性当成语法糖来用(比如在Python学习笔记(番外)——获取键盘输入事件这一篇里,用同一个变量名把bytes类型的值改成了str类型而不需要修改后续代码),当然动态类型有动态类型的缺点,python为了解决这个问题甚至利用了一些方法去接近静态类型语言的特性(通过Generic AliasUnion规定函数参数类型跟返回值类型)。

Python内置类型

参考python官网, python内置类型可以分为以下几类:

数值类型

数值类型的内置类型有:int, float, complex,int跟float就不多做赘述了,一个有趣的点是python的bool类型是继承自int的,另外int类型具有无限精度(内置大数计算),float类型实现的是C的double类型,大大方便了我们编程,当然会存在内存开销的问题,但是这个就交给官方优化吧(如果你足够牛逼并且对官方的内存优化不满的话可以自己尝试修改)。除了以上两个常见类型以外,python还内置了复数类型complex,对于某些领域应该有用吧,单纯的当作二维向量的话,我还是愿意用一些vector库。
常见的运算符(+-*/%)就不提了,python还额外提供了//**运算符,其中//代表除并向下取整,**代表指数运算,x ** ypow( x , y )等价,一种语法糖吧。
此外,对于int类型来说,常见的位运算也是全部支持的(& | ~ ^ << >> )。

序列类型

list tuple range,除了这三个之外,str bytes bytearray memoryview也属于序列类型,所以它们也具有序列类型的特性。
序列类型可以使用以下运算符跟函数:

运算符 作用
x in s 判断元素x是否在序列s中
s1 + s2 返回s1跟s2连接的结果,如[1,2]+[3],结果为[1,2,3]
s * n n * s 注意n为int,返回s重复n次的结果,[1,2]*2,结果为[1,2,1,2]
s[i] 这个不用说了
s[i:j] 序列s从i到j的子序列,包含i,不包含j,即[1,2,3][0:1]结果为[1]
s[i:j:k] 同上,但是以k为步长
len(s) 不用说了
min(s) 作用不用说了,注意当s的元素不是数值类型时该函数也是可以使用的,只要元素类型一致,个人猜测跟元素的__ge__ __gt__函数有关
max(s) 同上
s.index(x) 不用说了,注意此处可以使用可选参数s.index(x,i, j),表示从i到j搜索,依然是包含i不包含j
s.count(x) 同上

list不用多做介绍了吧,类似C的数组或者java的array,只是不用严格地定义大小并且方便操作,由于python弱类型的特性,一个序列内可以存放不同的类型,类似[ 1, '1' , True]这样的list都是允许的,当然这样会使min max函数失效。
tuple即元组类型,跟list的区别在于tuple一经初始化,无法修改,使用()定义而非[]
range在for循环中比较常见,类似for i in range(0,100)这样的用法,使用range(i , j , k),可以生成一个等差数列,参数k可选,默认为1,同样包含i不包含j,也可以仅传递一个参数,range(10)等价于range(0 , 10),可以强制转换为list类型,如下:

print(list(range(0, 1)))

可以看到输出是[0],如果不转换的话,输出的是range(0,1)

字符串——str

另外,如前所述,str类型也是一种序列,可以使用所有序列类型的运算符,所以类似'gg' in 'egg'或者'egg'.index('gg')这样的代码都是可以正常运行的,相对于C/C++的strcpy strcat之类的函数,使用+这样的运算符自然是方便不少,当然这种语法糖也不新鲜了,java都有。另外,str是一种immutable type,所以它的内容一经定义也是无法修改的。
str有4种定义方式:

'This is a string'
"This is a string"
'''
This is a string
'''
"""
This is a string
"""

可以看到,单引号跟双引号是等价的,而三个连续的引号可以定义多行文本,一个有趣的点是,双引号内部用单引号不用转义,反之亦然。另外,在字符串前面加字母r,代表这是一个raw string,内部任何字符都是它本来的意思而不用考虑任何转义,但是一个操蛋的点在于如果raw string是以\结尾的,那么它将是非法的,且raw string内部不进行转义,所以\\也无法得到\

r'\' # 这是非法的
r'\\' # 合法,但输出结果是\\

实在要用\结尾的话,要不,还是拼贴一下?

r'test' + '\\' # 得到test\

对于长单行文本,过长的单行代码可读性差并且也不符合PEP8标准,比如为了写单词Preumonoultramicroscopicsilicovolcanoconiosis可以如此定义:

'Preumonoul' \
'tramicrosco' \
'picsilicovol' \
'canoconiosis'

有点像C/C++的写法,以上写法等价于java的:

"Preumonoul" + 
"tramicrosco" + 
"picsilicovol" + 
"canoconiosis"

但是python的换行缩进定义了代码块,所以java写法不可用。但是在作为参数时,由于不存在歧义,python又允许你这么用:

def foo(arg : str):
    print(arg)


foo(arg='Preumonoul'
         'tramicrosco'
         'picsilicovol'
         'canoconiosis')

好吧python,你是自由的。

熟悉java的程序员都知道字符串的比较不能简单地使用==,这会导致严重的问题,python则不会,以下写法在python中是正确的:

s = input('input test') # 输入test
print( s == 'test' ) # True

在java中,你不得不使用String.equals()函数来确保它的正确性,然而在python中,你可以尽情地使用==!=来进行判断,赞美python!

二进制序列类型

二进制序列类型有bytes bytearray memoryview,如前所述,这些类型都属于序列类型,所以序列类型中介绍的运算符依旧适用。
bytes类型的定义跟str一样,只是在前面加一个b:

b'This is a bytes'

bytearray则是bytes版的listbytearray之于bytes,就像list之于tuple一样,即bytes不可写而bytearray可写,bytearray用法跟list大差不差,也不多说了(懒)。
bytesbytearray可以通过decode函数转化为str,反之,str可以通过encode转化为bytes
memoryview就比较牛逼了,memoryview使用一个支持buffer protocol的对象(list bytes bytearray等)作为参数初始化:

momeryview(b'momoryview test')
momeryview(['meomryview',test])

然后可以通过memoryview查看、修改源对象的内容,注意:当源对象可写时,memoryview才可以进行修改,总的来说,python封装了一个方便你对对象(该对象必须支持buffer protocol)进行内存级别操作的类:

ba = bytearray(b'test')
mv = memoryview(ba)
mv[0:5] = b'tttt'
print(ba)

以上代码会输出b'tttt'

集合

set frozensetset就不多说了,都懂,使用{}定义:

{ 'set' , 'test' , 0 , 1 }

由于python弱类型的特性,set里面可以有各种不同类型。
frozenset顾名思义,就是冻结了的set,一个不可修改版本的set

映射集

dict这个不用介绍了吧,类似java的HaspMap,使用{}定义:

{'0' : 0}

同样由于python弱类型的特性,dict的kv对可以出现任何妖魔鬼怪,以下写法也是可行的:

{
    '0':0,
    0:'0',
}

当然一般不推荐这么做。

类型注解 Generic Alias,联合体

好吧我不知道怎么翻译这个Generic Alias,通用别名?总之,如前言所说,python作为弱类型语言,有时候需要一些规则来规定类型,Generic AliasUnion就是做这个事情的,常见应用如下:

def average(values: list[float]) -> float:
    return sum(values) / len(values)

def send_post_request(url: str, body: dict[str, int]) -> None:
    ...

对于容器类型,Generic Alias可以规定它的元素类型,对于dict,可以分别规定键值的类型。
根据python 3.10.5的文档,以下用法也是可行的:

t = list[int]
t([1, 2, 3])

这就很接近强类型语言了,终究是妥协了,不过我在3.8的环境下是会报错的,应该是比较新的特性。
Union也是3.9以后的新特性,不过跟C/C++的Union完全不是一回事,python的Union类似Generic Alias,也是一种类型注解:

def square(number: int | float) -> int | float:
    return number ** 2

并且具有如下规则:

(int | str) | float == int | str | float # 结合律
int | str | int == int | str # 冗余类型会被删除
int | str == str | int # 交换律
int | str == typing.Union[int, str] # 另一种写法
str | None == typing.Optional[str] # Optional可以表示为 | None
逻辑判断

万物皆对象,python中的类全都继承自内置类型object,继承了几个通用的内置函数,其中__bool__跟__len__会用来做逻辑判断,如果这两个函数都未被定义,那么这个类型在做逻辑判断时永远为真,即在如下代码中:

class test:
    pass


if test():
    print('True')
else:
    print('False')

if test()中将会把test()的值判断为True。
那么有趣的情况来了,如果我定义__bool__返回False,而__len__返回非零值,或者相反呢?

class test:
    def __bool__(self):
        return False

    def __len__(self):
        return 1


if test():
    print('True')
else:
    print('False')

这种情况下,程序输出False,即__bool__的优先级是比__len__高的。
对于python内置的基本类型判断为False的情况(基本类型也都是类,python重写了它们的__bool__或者__len__函数),官网给出了如下解释:

  • constants defined to be false: None and False.
  • zero of any numeric type: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
  • empty sequences and collections: '', (), [], {}, set(), range(0)

常量None和False,逻辑判断为False,数值型变量数值为0则判断为False,集合为空,则判断为False,其余情况均为True,当然从可读性角度来说,我个人倾向于除了bool类型的变量,全都使用==和!=判断(PEP8标准下,对于None应该使用is和is not),没必要追求早期程序员那种极致的简化风格。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342