循环语句
至少执行一次While
假设我们给出的条件一开始就是False,这就导致虽然写了While语句,但是却一次也没有执行,例如下面的例子
i = 5
while i < 5:
print(i)
i += 1
复制代码
如果我们想让While语句至少执行一次,该怎么做呢?例如Swift,有专门的语法可以实现
repeat {
statements
} while condition
复制代码
但是Python中没有这种语法,这时候我们就可以用到无限循环和break了
i = 5
while True:
print(i)
if i >= 5:
break
复制代码
while ... else 语句
当while中的语句正常地全部循环跑完,没有出现break,则运行else中的语句
假设存在一个列表l = [1,2,3]
和一个变量val = 10
,现在我们要检查列表中是否存在变量(用while语句实现),如果没有的话,把变量加进列表,于是有
l = [1,2,3]
val = 10
index = 0
found = False
while index < len(l):
if l[index] == val:
found = True
break
index += 1
if not found:
l.append(val)
复制代码
上面的代码中,为了检测变量val是否在列表l中,还必须给一个flag(found)来做判断,利用while...else语句就可以省略此步骤
l = [1,2,3]
val = 10
index = 0
while index < len(l):
if l[index] == val:
break
index += 1
else:
l.append(val)
复制代码
try...except..finally语句中遇到continue或者break的情况
通常情况下,循环中碰到continue或break,后面的语句就被忽略了,要么开始一段新的循环,要么就直接扑街了
这是新手一般学到的,但是有时遇到了些“流氓”语句,break和continue也只能乖乖听话,执行之后的语句
假设我们写一个循环,每执行一次,a就加1,b就减1,直到b变成了0,出现ZeroDivisionError
a = 0
b = 2
while a < 4:
a += 1
b -= 1
try:
print('--------------------')
a/b
except ZeroDivisionError:
print('oops, b is equal 0')
contine
finally:
print('i am everywhere')
复制代码
在这个过程中,b肯定会变成一次零,于是触发except,执行内部的语句,这时候finally语句中的内容还会存在吗?看看那输出结果
--------------------
i am everywhere
--------------------
oops, b is equal 0
i am everywhere
--------------------
i am everywhere
--------------------
i am everywhere
复制代码
看!!i am everywhere
这条语句还是被执行了一遍,这下懂了,finally才是大哥,无视你continue不continue的,同理,break也一样
知道这个行为后,以后我们保存数据库文件等内容就可以交给finally了,不会怕出现break导致数据丢失啦
for 循环语句 与 iterable
在别的语言中,例如js,for 循环语句一般长成这样
var list_ = [1,2,3]
for (var i = 0; i < list_.length; i++) {
console.log(list_[i])
}
复制代码
在Python中,for循环长这样
list_ = [1,2,3]
for x in list_:
print(x)
复制代码
咋一看,好像没差吗,不都是for循环,只是语法不同罢了
其实真正情况不仅仅是语法不同而已,更重要的是机制完全不同
js中的for循环是设定自增的索引,从列表中逐个取出,是我们主动取出的
而python中的列表在for语句中不用设定索引,每次循环对象都自己返回一个值,这是python特有的一个概念,叫iterable
我们看看官方是怎么说的
An iterable object is an object that implements
__iter__
, which is expected to return an iterator object
当对象设定了__iter__
之后,并且每次都返回一个iterator
对象时,我们就说这个一个iterable
的对象,具体的在后面的内容我们会详细说明
除了列表是iterable,字符串,元组等也都是iterable
for...else语句
for...else语句和之前的while...else很像,只有当for的代码完全执行完,并且没有碰上break,就可以执行else内的代码
for i in range(1,5):
if i%7 == 0:
print('multiple of 7 found')
break
else:
print('no multiple of 7 in the range')
复制代码
输出
no multiple of 7 in the range
三种方法获取list的index
小火龙级别?
list_ = 'hello'
c = 0
for i in list_:
print(c,i)
c += 1
复制代码
火恐龙级别的??
list_ = 'hello'
for i in range(len(list_)):
print(i,list_[i])
复制代码
喷火龙级别的???
list_ = 'hello'
for i,j in enumerate(list_):
print(i,j)
复制代码
类
改写一些默认的方法
首先我们先定义一个Retangle
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2*(self.width + self.height)
r1 = Rectangle(20,30)
r2 = Rectangle(30,40)
复制代码
我现在突发奇想,想让每个Rectangle的实例可以字符串化,可以比较大小,自身返回的不再是类型,而是我设定的内容
也就是在ide中执行str(r1); r1 < r2; r1
在实施手术前,我们先看看默认做上面的事,pyhton会返回啥
'<__main__.Rectangle object at 0x10b286668>'
'''
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
r1 < r2
TypeError: '<' not supported between instances of 'Rectangle' and 'Rectangle'
'''
<__main__.Rectangle object at 0x10b286668>
复制代码
一个都不是我们想要的的,所以我们要对内部进行重新改造
下面是改造之后的样子
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2*(self.width + self.height)
def __str__(self):
return "this is an rectangle with \
width:{0},height:{1}".format(self.width,self.height)
def __lt__(self,other):
if isinstance(other,Rectangle):
return self.area() < other.area()
else:
return NotImplemented
def __repr__(self):
return "Rectangle: ({0}, {1})".format(self.width,self.height)
r1 = Rectangle(20,30)
r2 = Rectangle(30,40)
复制代码
让我们重新执行上面的代码,看看会发生什么
'this is an rectangle with width:20,height:30'
True
'Rectangle:(20,30)'
复制代码
手术成功?
避免外部随意修改内部属性
(⚠️以下的代码仅仅对宽度进行了分析,高度其实一样的,大家可以自己分析下)
有时候我们建立一个实例时,没有考虑安全因素,内部的属性会被随意修改,比如说下面这个长方形
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
def __repr__(self):
return "Rectangle({0},{1})".format(self.width,self.height)
r1 = Rectangle(30,40)
复制代码
为了输出方便我们对__repr__
进行了重写,这样返回的描述内容就不是类型和内存地址了,是定义的内容
>>> r1
Rectangle(30,40)
>>> r1.width
30
>>> r1.width = -30
>>> r1.width
-30
>>> r1
Rectangle(-30,40)
复制代码
从上面我们可以看出,建立一个实例后,哪怕给宽度设置为负数,都是可以的,别说是数字了,你甚至可以给它一个字符串
>>> r1.width = "i am width"
>>> r1
Rectangle(i am width,40)
复制代码
这样下去可不行,不如我们把它.....
这样写一个类就太随意了,为了安全起见,我们还是给这个类添加setter和getter吧
class Rectangle:
def __init__(self,width,height):
self._width = width
self._height = height
def get_width(self):
return self._width
def set_width(self,width):
if width > 0:
self._width = width
else:
raise ValueError('width can\'t be negative')
def __repr__(self):
return "Rectangle({0},{1})".format(self._width,self._height)
r1 = Rectangle(30,40)
复制代码
为了不让外部随意获取我们的实际宽度,我们设定了“私有”属性
(⚠️python中没有私有这个概念,前面加下划线的写法只是一种约定成俗的规定)
这样我们来看看运行的结果
>>> r1
Rectangle(30,40)
>>> r1.get_width()
30
>>> r1.set_width(-40)
Traceback (most recent call last):
File "<pyshell#36>", line 1, in <module>
r1.set_width(-40)
File "/Users/juniortang/Documents/change_class_method.py", line 13, in set_width
raise ValueError('width can\'t be negative')
ValueError: width can't be negative
>>> r1.set_width(40)
>>> r1
Rectangle(40,40)
复制代码
耶✌️,再也不用担心我的宽度变负数了
但是人家又不知道你的这个类里面有这个实例方法,默认情况下大家还是会这样写
>>> r1.width = -30
复制代码
然而呢,当按下回车键的时候,Python居然没有报错,即使现在的实例里面根本没有width这个属性
这其实是一个python的特性,即使实例里面没有某个属性时,你也可以对这个实例添加这个属性,并且对其赋值
>>> r1.width
-30
复制代码
这就麻烦了
那有没有方法可以让width像函数一样,判断输入的内容呢
当然有的,那就是@property装饰器
将之前的类,改写为
class Rectangle:
def __init__(self,width,height):
self._width = width
self._height = height
@property
def width(self):
return self._width
@width.setter
def width(self,width):
if width > 0:
self._width = width
else:
raise ValueError('width can\'t be negative')
def __repr__(self):
return "Rectangle({0},{1})".format(self._width,self._height)
r1 = Rectangle(30,40)
复制代码
通过添加装饰器,我们的属性就可以像函数一样添加判断了,我们来看下里面的内容
>>> r1
Rectangle(30,40)
>>> r1.width
30
>>> r1.width = -60
Traceback (most recent call last):
File "<pyshell#46>", line 1, in <module>
r1.width = -60
File "/Users/juniortang/Documents/change_class_method.py", line 15, in width
raise ValueError('width can\'t be negative')
ValueError: width can't be negative
复制代码
好像还不错,但是如果我们一开始制造实例的时候,就对宽度赋予了负值,程序不会报错,会自己跑下去
>>> r2 = Rectangle(-40,60)
>>> r2
Rectangle(-40,60)
复制代码
其实这里我们只要修改__init__里面内容即可,只要将下划线去除,当新建实例时,里面的width就会采用自身的带有装饰器的width
def __init__(self,width,height):
self.width = width
self.height = height
复制代码
看看实验结果
>>> r2 =Rectangle(-40,50)
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
r2 =Rectangle(-40,50)
File "/Users/juniortang/Documents/change_class_method.py", line 3, in __init__
self.width = width
File "/Users/juniortang/Documents/change_class_method.py", line 15, in width
raise ValueError('width can\'t be negative')
ValueError: width can't be negative
复制代码
结尾
写完《基础复习》这两篇文章时
我自己对Python的理解又加深了一些
有一些内容自己以前都容易忽略
自己写写文章印象也更加深刻了
这些都是热身,Python真正有意思的地方现在才刚刚开始
成绩好的人学习方法都是相似的,成绩不好的人各有各的学习方法
多写写,多实践,多练习,让知识成为自己的条件反射,这样才达到更高的层次,也能比更多的人看得更远