深入理解Python [2] 基础复习(下篇)

循环语句

至少执行一次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真正有意思的地方现在才刚刚开始

成绩好的人学习方法都是相似的,成绩不好的人各有各的学习方法

多写写,多实践,多练习,让知识成为自己的条件反射,这样才达到更高的层次,也能比更多的人看得更远

转载于:https://juejin.im/post/5bc7df70e51d450e7e51c577

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值