Python学习笔记

数据类型和变量

Python支持多种数据类型,在计算机内部,可以把任何数据都看成一个“对象”,而变量就是在程序中用来指向这些数据对象的,对变量赋值就是把数据和变量给关联起来。对变量赋值x = y是把变量x指向真正的对象,该对象是变量y所指向的。随后对变量y的赋值不影响变量x的指向。

  1. python能直接处理的数据类型:整数、浮点数、字符串、布尔值、空值(None)、变量、常量
    **注意:Python的整数没有大小限制,而某些语言的整数根据其存储长度是有大小限制的,例如Java对32位整数的范围限制在-2147483648-2147483647。Python的浮点数也没有大小限制,但是超出一定范围就直接表示为inf(无限大).

  2. python中可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,这种变量本身类型不固定的语言称之为动态语言;静态语言在定义变量时必须指定变量类型,如果赋值时类型不匹配,就会报错,例如Java就是静态语言。

  3. 不可变数据对象:整型数值、浮点型数值、字符串、常量、布尔型 可变数据对象:list、dict
    注意元组是不可变对象,但其可以包含可变对象如tup = (1,2,[1,2,3]) 包含了可变对象[1,2,3]

函数的参数

注意:定义默认参数要牢记一点:默认参数必须指向不变对象!
原因:不变对象一旦创建,对象内部的数据就不可修改,减少了由于数据修改而造成的错误。

def add_end(L = []):
    L.append("end")
    return L
# 第一次调用函数
print(add_end())
# 第二次调用函数
print(add_end())
# 第三次调用函数
print(add_end())

结果:

[‘end’]
[‘end’, ‘end’]
[‘end’, ‘end’, ‘end’]

如果改成 L= None 即 形参指向不可变对象

def add_end(L = None):
    if L == None:
        L = []
    L.append("end")
    return L
# 第一次调用函数
print(add_end())
# 第二次调用函数
print(add_end())
# 第三次调用函数
print(add_end())

结果为:

[‘end’]
[‘end’]
[‘end’]

可变参数
*nums表示将nums所表示的列表或者元组中的元素分别做为形参传入函数,形参前加 * 表示将参数组合成列表或者元组传入函数中

def cals(*nums):
    s = 0
    for i in nums:
        s = s + i
    return s
nums = [1,2,3,4]
print(cals(1,2,3,4))
print(cals(*nums))

关键字参数
可变参数允许传入0个或任意个参数,这些可变参数自动组装成tuple,而关键字参数允许传入0个或任意个带参数名的参数,这些参数自动组装成dict。

def person(name, age, **kw):
    print('name', name, 'age', age, 'other', kw)
other = {'city': 'china', 'skill': 'python'}
person('Adam', 30, **other)

结果

name Adam age 30 other {‘city’: ‘china’, ‘skill’: ‘python’}

**other表示把other这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是other的一份拷贝,对kw的改动不会影响到函数外的other。

参数组合
在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

def f1(a, b, c=0, *args, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)

对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。

递归

  1. 递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
  2. 使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。因此要尽量写成尾递归的形式。
  3. 尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
# 斐波那契数列 1,1,2,3,5,8.... 求其第 n 项
# 普通递归
def fi(n):
    if n == 1 or n == 2:
        return 1
    return fi(n-1) + fi(n-2)
print(fi(7))

# 尾递归 注意两个出口条件 num1, num2
def fi(n, num1, num2):
    if n == 1:
        return num1
    if n == 2:
        return num2
    return fi(n-1, num2, num1 + num2)

print(fi(7, 1, 1))

迭代

python中采用for… in…来迭代可迭代对象,如list, dict, tuple
如何判断是否为可迭代对象:

from collections.abc import Iterable
print(isinstance([1,2,3], Iterable))
print(isinstance((1,2,3), Iterable))
print(isinstance('123', Iterable))
print(isinstance(123, Iterable))

True
True
True
False

生成器

  1. generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误
>> g = (x for x in range(3)) # 将生成式[]变成()即变成一个生成器对象 
>>> g  # 不断调用next可获得生成器对象的元素,当所有元素取完后,继续调用next将报StopIteration
<generator object <genexpr> at 0x000001BFC1CBDE08>
>>> next(g)
0
>>> next(g)
1
>>> next(g)
2
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

generator函数

# 以斐波那契数列为例,打印斐波那契数列,普通函数
def fi(max):
    n, a, b = 0, 0, 1
    while(n<=max):
        print(b)
        a, b = b, a+b
        n = n+1
    return 'done'
# generator函数
def fi1(max):
    n, a, b = 0, 0, 1
    while(n<=max):
        yield b 
        a, b = b, a+b
        n = n+1
    return 'done'

调用生成器函数产生一个生成器对象o,调用next(o) 获取该生成器对象的值,遇到yield就中断并返回一个值,下次继续调用next 获得下一个值
**注意:**调用generator函数会创建一个generator对象,多次调用generator函数会创建多个相互独立的generator。

def fi1(max):
    n, a, b = 0, 0, 1
    while(n<=max):
        yield b 
        a, b = b, a+b
        n = n+1
    return 'done'
f = fi1(4)
print(next(f))
print(next(f))
print(next(f))
print(next(f))
print(next(f))
print(next(f))
>>> 1
1
2
3
5
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-13-8408b21d6099> in <module>()
     12 print(next(f))
     13 print(next(f))
---> 14 print(next(f))

StopIteration: done

例子
杨辉三角定义如下:

        1
       / \
      1   1
     / \ / \
    1   2   1
   / \ / \ / \
  1   3   3   1
 / \ / \ / \ / \
1   4   6   4   1

把每一行看做一个list,试写一个generator,不断输出下一行的list:

# 原来的写法
def tri(n):
    i = 0
    while i < 2:
        yield [1]*(i+1)
        i = i+1
    lis = [[1]*i for i in range(1, n+1)]  # 注意定义二维数组的位置
    for i in range(2, n):
        for j in range(i+1):
            if i > j and j != 0:
                lis[i][j] = lis[i-1][j-1] + lis[i-1][j]
        yield lis[i]
    return 'done'
    
# 更加优雅的写法
def triangles(max):
    n = 0    
    l = [1]
    while n < max:
        yield l
        a = [1]   # 必须在yield之后
        b = [l[i] + l[i + 1] for i in range(len(l) - 1)]
        a.extend(b)
        a.append(1)
        l = a
        n += 1    
    return 'done'

迭代器

  1. 凡是可作用于for循环的对象都是Iterable类型;

  2. 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

  3. 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

可迭代对象如list、dict、tuple、set是可迭代的(iterable)但是他们不是迭代器(iterator),即他们不能用next()函数返回下一个数据元素,原因是Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

高阶函数

变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数,即将一个函数作为参数传给另一个函数。把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。

返回函数

继承和多态

不同于静态语言的继承和多态,动态语言具有鸭子类型特性,比如如下方法在接收一个对象作为参数时,静态语言只接受实现了read()方法的此对象及其子类,而python中却没有如此严格的规定,只要一个对象且实现了read()方法,way()方法都能被正确执行:

def way(object):
    object.read()

实例属性和类属性

为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:

class Student(object):
    count = 0

    def __init__(self, name):
        self.name = name
        # class内部的变量不是简单的引用关系,
        # 用global或nonlocal声明都无效,
        # 类属性调用必须使用类名.属性名进行调用,
        # 无论是在封装的内部还是在外部都一概如此。
        Student.count += 1   

使用@property

Python内置的@property装饰器就是负责把一个方法变成属性调用的:

class Student(object):

    @property  # 将方法变成实例属性 score
    def score(self):
        return self._score  # 注意返回的实例变量不能和方法名一致,否则会导致无限递归

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value
        
s = Student()
s.score = 100
s.score
>>> 100

属性的方法名不要和实例变量重名。例如,以下的代码是错误的:

class Student(object):

    # 方法名称和实例变量均为birth:
    @property
    def birth(self):
        return self.birth
s = Student()
s.birth
>>> RecursionError: maximum recursion depth exceeded while calling a Python object

这是因为调用s.birth时,首先转换为方法调用,在执行return self.birth时,又视为访问self的属性,于是又转换为方法调用,造成无限递归,最终导致栈溢出报错RecursionError。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值