Python基础知识(2)

二. 基础知识(2)

三大语句:

1.顺序语句

默认情况下,Python代码的执行顺序,是从上到下依次执行的

而选择语句和循环语句,则为更多了选择提供了可能

2.条件语句:

(1) if

(2) if - else

(3) if - elif - else

ps : Python中条件语句的写法和多数编程语言都不太一样

①if 后面的条件表达式,没有(),使用:作为结尾

②if / else 命中后面要执行的语句块,使用缩进,而不是{}

③对于多条件分支,不是写作else if,而是elif(合体了)

直接上代码

#让用户输一个数字,如果输入1,表示愿意认真学习,输入2表示躺平摆烂
choice = input("输入 1 表示愿意认真学习,输入 2 表示躺平摆烂:")
if choice == '1':
    print('能够找到好工作')
else:
    print('你可能毕业就失业了')

但是上述代码仍然存在漏洞.在实际开发中,用户的输入很有可能出乎意料,因此当用户输入其他数时,我们应该给以提示

#让用户输一个数字,如果输入1,表示愿意认真学习,输入2表示躺平摆烂
choice = input("输入 1 表示愿意认真学习,输入 2 表示躺平摆烂:")
if choice == '1':
    print('能够找到好工作')
elif choice == '2':
    print('你可能毕业就失业了')
else:
    print('您的输入有误')

----------------------Python中的缩进---------------------

a = input("请输入一个整数")
if a == '1':
    print('aaa')
    print('bbb')

输入1,打印 aaa 和 bbb

输入其他,什么都不打印

a = input("请输入一个整数")
if a == '1':
    print('aaa')
print('bbb')

输入 1,打印 aaa 和 bbb

输入其他,打印 bbb

Python中,if/else/elif/while/for 等等需要跟上代码块的部分,都是用缩进表示的
在C++/Java中,代码缩进没有强制要求,但是会影响代码风格
而Python则是做了硬性规定,没按要求缩进则会报错

Python中的代码还是可以嵌套的

a = input("请输入第一个整数")
b = input("请输入第二个整数")
if a == '1':
    if b == '2':
        print('aaa')
    print('bbb')
print('ccc')

这段代码大家自行理解即可,体会Python中缩进决定了当前语句属于哪个代码块

ps: 嵌套层数不宜太多,否则语句对应哪一代码块是不容易观察的,容易出错

····························条件语句练习题:

1.判断奇数/偶数

n = int(input())
if n % 2 == 0:
    print('偶数')
else:
    print('奇数')
n = int(input())
if n % 2 == 1:
    print('奇数')
else:
    print('偶数')

 这两段代码都是OK的

但是要注意的是在C++/Java中,第二段代码是存在问题的,如下图所示

Python基础知识(2)_第1张图片

因为在Python中 -5 % 2 =>1

而在C++/Java中,-5 % 2 =>-1

2.判断正负数

n = int(input())
if n > 0:
    print('正数')
elif n < 0:
    print('负数')
else:
    print('0')

注意考虑输入为0的情况

3.判断闰年

闰年的判定:

# 每隔 4 年 闰一次

# 如果年份仍能够被100整除,这是世纪闰年,得看能否被400整除!

写法一:
year = int(input('请输入一个年份'))
if year % 100 == 0:
    #世纪闰年的判定
    if year % 400  == 0:
        print('闰年')
    else:
        print('平年')
else:
    if year % 4 == 0:
        print('闰年')
    else:
        print('平年')
#写法二:
n = int(input('请输入一个年份'))
if (n % 4 == 0 and n % 100 != 0) or n % 400 == 0:
    print('是闰年')
else:
    print('平年')

虽然代码二比较短,但是中间夹杂了一些更复杂党的条件判定,因此代码一可读性还是较高的,不能只根据代码的长短来评定代码的好坏
 

下面再看一个例子

#输入1打印 hello ,否则什么都不打印
n = input()
if n == '1':
    print('hello')

这段代码非常简单,但是有些同学想着把条件反着写,如果n != 1 就什么也不打印,否则打印 hello

Python基础知识(2)_第2张图片

 可以看到,这样的写法是有问题的,不符合Python的语法要求,怎么解决这个问题呢?

这个时候便要用到pass语句,pass语句被称为空语句,没有实际的意义,但是可以来占位

n = input()
if n != '1':
    pass
else:
    print('hello')

2.循环语句:

①while语句

while 条件:

      代码块

eg:

1.打印1-10

num = 1
while num <= 10:
    print(num)
    num += 1

while 循环三要素 : 循环变量的初始值;循环的判定条件;循环变量的语句更新

ps:若忘记 num += 1 这条语句,则为造成死循环.但是要说明的是死循环很多时候是bug,但有些时候不是bug(服务器要一直处于运行状态因为不知道客户什么时候需要连接过来)

2.计算1到100的和

num = 1
sum = 0
while num<=100:
    sum += num
    num += 1
print(f'sum = {sum}')

3.5的阶乘

num = 1
ret = 1
while num <= 5:
    ret *= num
    num += 1
print(f'ret = {ret}')

4.求1! +2! +3! +4! +5!

法一:

num = 1
sum = 0
while num<=5:
    ret = 1
    i = 1
    while i <= num:
        ret *= i
        i += 1
    sum += ret
    num += 1
print(f'sum = {sum}')

法二:

num = 1
ret = 1
sum = 0
while num <= 5:
    ret *= num
    sum += ret
    num += 1
print(f'sum = {sum}')

②for 循环

基本格式 : 

for 循环变量 in 可迭代对象

      循环体

ps:

1.Python中的for循环和其他语言不同,没没有初识化语句,没有循环条件判定语句,也没有循环变量更新语句,而是更加简单

 2.所谓的可迭代对象,指的是"内部包含多个元素,能一个一个把元素取出来的特殊变量"

例题:

1.打印1到10

for i in range(1,10):
    print(i)
     

range 是 Python 的一个内建函数,起到的效果就是得到了一个"可迭代对象",这个可迭代对象就包含了一系列的整数.  (range可以生成一个包含整数序列的可迭代对象~)      range(begin,end)   =>   [begin,end)前闭后开

那如果是打印 2,4,6,8 呢?

range 还提供了第三个参数~表示步长,默认的步长是1

for i in range(2,10,2):
    print(i)

那如果想倒着打印(1-10)呢? ------步长设为-1

for i in range(10, 0, -1):
    print(i)

2.求1 + 2 + ····+100

theSum = 0
for i in range(1, 101):
    theSum += i
print(f'theSum = {theSum}')

开始定义存和变量 sum,Python解释器会有提示,表示和内建函数名冲突了,后续就无法再使用内建函数sum了,因为我们要重命名

ps : 快捷键:shift + F6 ( + fn),会智能分析代码,自动把所有需要修改的名字都统一替换了

------------------continue 与 break

continue: 立即结束当前这次循环,进入下一次循环

break: 立即结束整个循环

案例:

# 计算用户输入任意个数字的平均数,以用户输入分号作为结束标志
theSum = 0
count = 0
while True:
    num = input("请输入一个数字(分号表示输入结束):")
    if num == ';':
        break
    num = float(num)
    theSum += num
    count += 1
print(f'平均值为:{theSum / count}')

函数

一段可以被重复使用的代码

ctrl + c ,ctrl + v 不就可以直接复制嘛?

1.复制代码,必须经过非常仔细的进行细节调整,尤其是数据不同的情况下

2.一旦复制过代码,需要调整,复制了几份就得调整几次~(因为我们也不知道这个代码被复制了几份)

因为,尽量还是不要复制代码,尽量做好代码"复用"

eg1:求 1- 100的和(上文已经介绍过)

eg2:求 300 - 400 的和

eg3:求1 - 1000 的和

可以发现,除了range里面的begin与end不同之外,其余都是一样的

因此可以定义一个函数,来实现代码的复用

#定义一个求和函数
def calSum(beg, end):
    theSum = 0
    for i in range(beg, end + 1):
        theSum += i
    print(theSum)
#调用函数
#求 1-100的和
calSum(1,100)
#求 300-400的和
calSum(300, 400)
#求 1-1000的和
calSum(1, 1000)

函数具体语法:

1. 函数定义:创建函数(分配任务)

def 函数名(形参列表):

      函数体

return 返回值

ps:

ps1:函数名与变量命名规则类似,软性规则与硬性规则~

ps2:形参列表:有多个形参,多个形参之间采用逗号分隔

ps3:函数返回值:函数执行到此就结束了,不过return 语句并不是必须有的

2. 函数调用:使用函数(开始完成任务)

实际参数简称形参,形参个数要和实参个数匹配,但是Python中,形参类型和实参可以不匹配(上文提到过的Python动态类型特点)(C++/Java必须要匹配),但是也不是说,随便传什么参数都行,只要保证传入的参数在函数体内能够支持对应的运算操作即可(比如下面第二段代码中字符串和数字本身就不能相加,这样传肯定报错)

def test(a):
    print(a)


test(10)
test('hello')
test(True)
def add(x, y):
    return x + y


print(add(10, 20))
print(add(1.5, 2.5))
print(add('hello', 'world'))

上面两段代码充分说明了Python中函数的形参和实参类型可以不匹配这一特点

函数的输入和输出

eg:求beg, end 这个范围内的整数之和(上文已经写过代码)

#求 beg, end这个范围的整数之和
def calSum(beg, end):
    theSum = 0
    for i in range(beg, end + 1):
        theSum += i
    print(theSum)


calSum(1, 100)

这段代码我们把打印功能放在了求和函数里面,而我们还有第二种写法

#求 beg, end这个范围的整数之和
def calSum(beg, end):
    theSum = 0
    for i in range(beg, end + 1):
        theSum += i
    return theSum


result = calSum(1, 100)
print(result)

···········拓展:

这段代码中,在calSum内部,只是进行了计算,而把打印的逻辑放在了函数外面,calSum把计算结果当做返回值,返回给"函数调用者"

在实际开发中,一般都倾向第二种写法,因为有一个通用的编程原则:一个函数只做一件事情

第一个版本既做了计算,又做了打印(和用户进行交互)

第二个版本,只做了计算,不关心如何和用户进行交互

一旦后续需要改变和用户交互的方式,第二种写法就更具有优势(不必修改代码了),当前是通过控制台和用户进行交互的~

让逻辑和交互/界面分离

进一步的好处:解耦合

一个稍微复杂一些的程序中,经常会涉及到多个模块,模块之间可能要进行交互,交互就会带来耦合,当然希望通过良好的设计让耦合尽量低

ps:
 

1.如果只是定义函数,而不去调用,则函数体里面的代码不会被执行,函数调用才会真正执行函数体里面的代码

2.函数定义一次后,可以多次调用

3. Python中要求,函数定义必须写在函数调用前面,否则就会报错

3.函数返回值

一个函数可以有多个 return 语句

def test():
    return 1
    return 2

这种情况显然不算有多个return语句的情况,因为执行到一个return 语句函数就结束了

多个return语句常见于函数里面有分支语句的情况

eg:判断奇数

def isOdd(num):
    if num % 2 == 0:
        return False
    else:
        return True
    
    
print(isOdd(10))
print(isOdd(9))
def isOdd(num):
    if num % 2 == 0:
        return False
    return True


print(isOdd(10))
print(isOdd(9))

仔细体会,发现上面两段代码是等价的

第二段代码本来 意思是无论if 条件是否满足,都会执行return True,但是由于if条件满足后,就会执行到return语句,没有机会执行return True了。

Python中一个函数可以返回多个值(C++/Java无法直接做到,但也有其他手段)

(C++ :要想返回多个值,可以通过输出型参数(指针/引用))

(Java要想返回多个值,需要把多个值给包装成一个对象,返回这个对象)

eg:

#写一个函数,返回平面上的一个点:
def getPoint():
    x = 10
    y = 20
    return x,y


a, b = getPoint()

虽然现在返回了多个值,但是我只想用其中的一部分,不关注其他的,可以使用 _ 来占位

例如上述代码中,我只想要y,不想要x,此时最后一句代码便可以写成 _, b = getPoint()

4.变量的作用域:

def getPoint():
    x = 10
    y = 20
    return x, y


x, y = getPoint()
print(x, y)

函数里面的x 和 y 和函数外面的x y 是同一组变量吗?

是不同的变量!只不过名字恰好相同!

一个变量名的有效范围是一定的!只在一个固定的区域内生效,如下图所示

Python基础知识(2)_第3张图片

函数内部的变量名只能在函数内部使用,出了函数就无效了

Python基础知识(2)_第4张图片

 在这个函数中,出现了两个名字一样的变量,函数内部的是局部变量,外面的是全局变量。

但是函数内部也是可以使用全局变量的~

x = 10


def test():
    print(f'x = {x}')


test()

在函数中尝试访问某个变量的时候,会先尝试在局部变量里面查找,如果找到就访问局部变量,如果没找到,就会往上一级查找(该段代码中上一级就是全局了)

读取操作容易,但是修改就没那么容易了

x = 10


def test():
    x = 20


test()
print(x)

发现x仍然是10,因为在函数内部 x = 10 相当于创建出了一个新的变量,并没有修改全局x

此处可以使用 global 关键字,相当于声明了x是全局变量

x = 10


def test():
    global x
    x = 20


test()
print(x)

这样便达到了在函数内部修改全局变量的目的

ps: if/else/while/for 这些关键字也会引入“代码块”,但是这些代码块不会对变量的作用域产生影响!!在上述语句代码块内部定义的变量也可以在外面被访问.

这点Python和Java/C++是不一样的,在Python中,只有函数和类才会影响作用域,而在C++/Java中,只要有{},就会影响作用域

for i in range(1, 11):
    pass
print(i)
if True:
    x = 10
print(x)

这些代码都是OK的,等等······

函数执行的过程就很简单了,就是当执行到函数调用的时候,就会调转到函数体内部来执行,当函数执行完毕就是回到之前调用的位置,继续往下执行~

如何观察函数执行的逻辑呢?---调试!!!(程序员的必备技能)

在某一条语句打上断点,然后鼠标右击'debug'选项,就可以进行调试执行,然后再下面选项中点击step into(F7)就可以让代码"单步运行"

相比于正常的运行,调试执行的最大的区别就是可以随时停下来,方便程序员观察程序的中间过程

5.函数链式调用:一个函数的返回值作为另外一个函数的参数

eg1:

在判断一个数是否为奇数的函数中

def is_odd(num):
    if num % 2 == 0:
        return False
    return True


result = is_odd(9)
print(result)

上面的最后两行完全可以写成 print(is_odd(9)),这便是函数链式调用

当然链式调用也可以有多个层级

eg2:

def is_odd(num):
    if num % 2 == 0:
        return False
    return True


def add(x, y):
    return x + y


print(is_odd(add(3, 5)))

链式调用中,是先执行()的函数,后执行外面的函数,换句话说,调用一个函数,需要先对它的参数求值

ps:链式调用的层次太深!否则会影响可读性

6. 函数嵌套调用:

eg1:

# 最简单的嵌套调用
def test():
    print('hello')


test()

eg2:

# 多层嵌套调用
def a():
    print('函数a')


def b():
    print('函数b')
    a()


def c():
    print('函数c')
    b()


def d():
    print('函数d')
    c()


d()

结果:

Python基础知识(2)_第5张图片

ps:稍加修改,代码的调用逻辑就截然不同(借助画图/调试理解)

变式:(深刻理解函数执行的逻辑)---从哪里来到哪里去

# 多层嵌套调用
def a():
    print('函数a')


def b():
    a()
    print('函数b')


def c():
    b()
    print('函数c')


def d():
    c()
    print('函数d')


d()

结果:

Python基础知识(2)_第6张图片

7. 函数栈帧:

def a():
    num2 = 20
    print('函数a')


def b():
    a()
    num3 = 30
    print('函数b')


def c():
    num3 = 30
    b()
    print('函数c')


def d():
    num4 = 40
    c()
    print('函数d')


d()

调试起来之后,在调试器的坐下方就会看到函数之间的"调用栈",调用栈里面描述了当前这个代码的函数之间的调用关系是啥,每一层这个调用关系就被成为"函数的栈帧",每个函数的局部变量就在这个栈帧中体现的~

Python基础知识(2)_第7张图片

 而每一层栈帧,选中之后都能看见其中的局部变量,每个函数的局部变量就保存在对应的函数栈帧中

调用函数,则生成对应的函数栈帧,函数结束,则对应的函数栈帧消亡(局部变量也随之销毁)

如果把上面的变量名全部都改为 num,他们仍然是属于不同的变量,变量就是一块内存空间,每个变量的都是保存在各自的栈帧之上的,而每个栈帧也是位于不同的内存空间上

8. 函数递归:

函数自己调用自己

eg:

# 写一个函数采用递归方式求n的阶乘(n是正整数)
def factor(n):
    if n == 1:
        return 1
    else:
        return n * factor(n - 1)


print(factor(4))

递归的代码往往看着写法很简单,但是实际的执行过程可能会很复杂的(画图可以很好理解递归函数执行的逻辑/调试)

Python基础知识(2)_第8张图片

 递归代码两要素:

--------递归结束条件

--------递归的递推公式

递归缺点:

1.执行过程非常复杂,难以理解

2.递归代码容易出现"栈溢出"的情况(每调用一次函数,就会在栈区上为函数开辟空间(函数栈帧),而栈区空间是有限的,总有被用完的时候)

①没有递归跳出条件/递归的过程中没有接近递归跳出条件

tip1:无递归条件

Python基础知识(2)_第9张图片

 死递归

tip2:没有接近跳出条件

Python基础知识(2)_第10张图片

②递归层次太深

Python基础知识(2)_第11张图片

求第10个斐波拉契数时没有任何问题

当求第100个斐波拉契数的时候光标一直在闪烁,计算机可不会偷懒,它一直在卖力的运行,不过求第100个斐波拉契实在太费劲了,调用的函数次数已经太多了

Python基础知识(2)_第12张图片

 当求第1000个斐波拉契数的时候程序直接崩溃了,因为栈溢出了

Python基础知识(2)_第13张图片

 其实求斐波拉契数这一问题涉及到了时间复杂度的问题,学过时间复杂度还可以对其理解更加深刻,后面我会介绍数据结构中的时间复杂度,大家可以回头再看这一代码定会有更深刻理解

3.递归代码一般是可以转化为等价的循环代码的,并且循环版本通常运行速度要比递归版本更有优势

(函数调用也是有开销的)

递归的优点:

代码非常简洁,尤其是处理一些"问题本身就是按递归定义的"(如二叉树,后面文章进行讲解)

函数里面的参数默认值

前面讲函数形参是根据传入的实参确定的

但是我们也可以直接给函数形参一个默认值,传参的时候就不必传对应实参了

经典案例:

def add(x, y):
    return x + y


result = add(10, 20)
print(result)

希望在函数运行时方便参数传了什么,于是我们加上了打印信息

def add(x, y):
    print(f'x = {x}, y = {y}')
    return x + y


result = add(10, 20)
print(result)

在函数内部加上打印信息,可以方便我们进行调试

但是像这种调试信息,希望在正式发布的时候不要有,只是在调试阶段才有

def add(x, y, debug):
    if debug:
        print(f'x = {x}, y = {y}')
    return x + y


result = add(10, 20, False)
print(result)

于是我们给函数多加了一个参数,通过debug参数决定是否要打印

但是每次都要传第三个参数(False/True,看着比较别扭)

def add(x, y, debug=False):
    if debug:
        print(f'x = {x}, y = {y}')
    return x + y


result = add(10, 20)
print(result)

这个时候就可以用到默认值参数

带有默认值的形参,就可以在调用函数的时候不必传参,不传参就使用默认值,传参就使用传递的实参

通过这样的默认值,就可以让函数的设计更灵活----有的函数需要给调用者提供多种功能,要提供很多参数,但是参数越多使用成本就越高,调用函数的人就得研究每个参数是什么意思,传不同参数会有什么效果~~~

所有我们把一些参数设计为默认值,如果调用者只是简单使用一下,那么很多参数就不用关注,如果想深度影响函数行为,就可以添加一些更多的选项,手动指定参数来影响函数的内部行为

但是默认值这样的语法,在编程界存在争议

C++也支持默认参数,但Java就不支持

ps:要求带有默认值的形参,得在形参列表的后面,而不能在后面/中间,多个带有默认参数的形参,这些都得放在后面

Python基础知识(2)_第14张图片

关键字参数

前面讲的函数传参主要是按照位置先后顺序对应传参的(位置传参),这也是各个编程语言中最普遍的方式~~~

关键字传参---按照形参的名字进行传参

eg:

def test(x, y):
    print(f'x = {x}')
    print(f'y = {y}')


test(10, 20)

这是之前的写法

def test(x, y):
    print(f'x = {x}')
    print(f'y = {y}')


test(x=10, y=20)

现在我们在传参的时候非常明显的告诉了程序员,你的参数要传递给谁,另外可以无视形参和实参的顺序

def test(x, y):
    print(f'x = {x}')
    print(f'y = {y}')


test(y=10, x=20)

这样依旧可以照常打印x与y

位置参数和关键字参数还能混着用,只不过混着用的时候要求位置参数在前,关键字参数在后~~~

关键字参数也就是搭配默认参数来使用的

一个函数可以提供很多的参数,来实现对这个函数的内部功能做出一些调整设定,为了降低调用者的使用成本,就可以把大部分参数设定出默认值~~~

当调用者需要调整其中一部分参数的时候,就可以搭配关键字参数来进行操作~~~

你可能感兴趣的:(数学建模,python,开发语言)