type(True)
<class 'bool'>
实际上bool类型是int类型的一个子类。
id(object):查看地址
python中只支持字符形式的and、or、not逻辑运算,不支持符号类型的&&、||、!
。
虽然可以通过反斜线\
转义去调整字符串,但带上反斜线有时候会严重影响可读性。如果不想使用反斜线转义,可以考虑使用三引号包围,也可以使用r
来声明后面的字符串是raw字符串,这里面的所有字符都是字面意义的,不会进行任何转义。
因为python中的字符串是一种序列类型,所以可以使用in
来测试字符串中是否包含某个字符或子串。
>>> 'o' in "hello"
True
>>> 'a' in "hello"
False
>>> 'll' in "hello"
True
同样,因为是序列类型,可以使用for来遍历整个字符串:
>>> str = "longshuai"
>>> for i in str:
... print(i, end=",")
...
l,o,n,g,s,h,u,a,i,
注意,python中字符串是不可变对象,所以所有修改和生成字符串的操作的实现方法都是另一个内存片段中新生成一个字符串对象。例如
解析操作和for息息相关,且都能改写成for循环。例如,下面两个语句得到的结果是一致的:
[ i * 2 for i in "abcdef" ]
L = []
for i in "abcdef":
L.append(i * 2)
需要注意的是,python中很多地方隐式地自动构造元组。它的隐式构造规则是这样的:如果没有指定括号,又有多个值,那么就使用元组作为容器。
>>> 1,2
(1, 2)
再者,为了区分没有括号的元组和单个数据对象,可以在想要作为元组元素的数据后面加上逗号。例如:
>>> 1, # 元组
(1,)
>>> (1,) # 元组
(1,)
>>> 1 # 单个数据
1
>>> (1) # 单个数据
1
不可变序列的操作
相比可变序列,不可变序列的唯一操作是可以支持内置的hash()操作。可变序列无法hash()。
>>> hash("asd")
-2014632507
>>> hash((1,23))
1320437575
>>> hash([1,23])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
能够hash的不可变序列,意味着能作为dict的key,能保存到set或frozenset中。
hash()后打印的是id.
因此,a = 3
中,变量名a保存的是数据对象3的地址,之后可以为其赋值一个字符串a = "hello"
,这时a保存的是"hello"字符串的地址。在这个类型改变的过程中,a仅仅只是修改了一下地址而已。
条件表达式Condition部分以运算结果的True、False为if的分支执行依据。在Python中所有数据对象要么是True,要么是False,所以任何一个数据、表达式都可以作为if的Condition
先介绍一下range()。它像Linux下的seq命令功能一样,用来返回一些序列数值。range()返回一个可迭代对象,目前无需知道可迭代对象是什么,只需知道它可以转换成list、tuple、Set,然后可以在通用迭代器for中进行迭代。
>>> range(3)
range(0, 3)
>>> list(range(3)),set(range(3)),tuple(range(3))
([0, 1, 2], {0, 1, 2}, (0, 1, 2))
可见,range()返回的序列值是前闭后开的。
分析for + range迭代的过程
下面两个例子,在结果上是等价的:
for i in range(3):
print(i)
for i in [0,1,2]:
print(i)
但除了结果上,过程并不一样。range()既然返回可迭代对象,说明序列数值是需要迭代一个临时生成一个的,也就是说range()从始至终在内存中都只占用一个数值的内存空间。而[0,1,2]
则是在内存中占用一个包含3数值元素的列表,然后for从这个列表对象中按照索引进行迭代。
再通俗地解释下,for i in range(3)
开始迭代的时候,生成一个数值0,第二次迭代再生成数值1,第三次迭代再生成数值2,在第一次迭代的时候,1和2都是不存在的。而[0,1,2]
则是早就存在于内存中,for通过list类型编写好的迭代器进行迭代,每次迭代从已存在的数值中取一个元素。
这种效率的区别,也可以应用于其它迭代方式的分析上。例如,按行读取文件的两种方式:
for i in open("filename"):
print(i)
for i in open("filename").readlines():
print(i)
第一种方式,open()返回一个文件迭代器,每次需要迭代的时候才会去读需要的那一行,也就是说从始至终在内存中都只占用一行数据的空间。而第二种通过readlines()读取时,它会一次性将文件中所有行都读取到一个列表中,然后for去迭代这个列表。如果文件比较大,第二种方式可能会占用比较大的内存,甚至可能比原文件大小还要大,因为很可能会一次性为400M的文件分配500M内存,以免后续不断的内存分配。
for i in open("filename"):
print(i)
for i in open("filename").readlines():
print(i)
第一种方式,open()返回一个文件迭代器,每次需要迭代的时候才会去读需要的那一行,也就是说从始至终在内存中都只占用一行数据的空间。而第二种通过readlines()读取时,它会一次性将文件中所有行都读取到一个列表中,然后for去迭代这个列表。如果文件比较大,第二种方式可能会占用比较大的内存,甚至可能比原文件大小还要大,因为很可能会一次性为400M的文件分配500M内存,以免后续不断的内存分配。
函数声明、调用的过程详述
def用来声明一个函数,python的函数包括函数名称、参数、函数体、函数体中涉及到的变量、返回值。
实际上,函数名称其实是一个变量名,def表示将保存在某块内存区域中的函数代码体赋值给函数名变量。例如:
def myfunc(x,y,z):
...CODE...
上面表示将函数体赋值给变量名myfunc。如下图:
既然是变量,就可以进行输出:
def myfunc(x):
return x+5
print(myfunc)
输出结果:
<function myfunc at 0x032EA6F0>
实际上,变量的明确的值会当作常量被记录起来。如a=10
的10被作为常量,而变量a赋值给变量b时b=a
,a显然不会作为常量。
如下函数:
x=3
def myfunc(a,b):
c=10
print(x,a,b,c)
myfunc(5,6)
输出结果为:"3 5 6 10"。
上面的函数涉及到了4个变量:a、b、c、x。其中:
- 全局变量x
- 本地变量a、b、c,其中本地变量a和b是函数的参数
函数属性
python中的函数是一种对象,它有属于对象的属性。除此之外,函数还可以自定义自己的属性。注意,属性是和对象相关的,和作用域无关。
自定义属性
自定义函数自己的属性方式很简单。假设函数名称为myfunc,那么为这个函数添加一个属性var1:
myfunc.var1="abc"
那么这个属性var1就像是全局变量一样被访问、修改。但它并不是全局变量。
可以跨模块自定义函数的属性。例如,在b.py中有一个函数b_func()
,然后在a.py中导入这个b.py模块,可以直接在a.py中设置并访问来自b.py中的b_func()
的属性。
import b
b.b_func.var1="hello"
print(b.b_func.var1) # 输出hello
Holden_Liu 发布了31 篇原创文章 · 获赞 13 · 访问量 5592 私信 关注