【学习笔记】python

基础

  • print()每执行一次默认在最后加上了\n,也就是默认会换行;

  • break可以立即退出循环语句,包括else;pass是用来在循环或者判断中进行占位的(比如暂时没想好功能该怎么写,就先占位);

    • == != 比较的是对象的值是否相等;
    • is is not 比较的是对象的id是否相等(也就是比较是否是同一个对象)

序列

序列就是用来保存一组有序的数据,所有的数据在序列中都有唯一的索引;

  • 可变序列:列表(list);

  • 不可变序列:字符串(str),元祖(tuple);

    遍历序列

    for s in stus:

      print(s)     # 每执行一次将序列中的元素赋给变量s
      
    

    range(3,0,-1) #[3,2,1],都是左闭右开;range就是生成一个自然数的序列;
    for i in range(30):

      代码块    #循环30次
    

可变对象

每个对象都保存了三个数据:

  • id(标识/地址)
  • type(类型)
  • value(值)

列表就是可变对象,可变对象是说其值(value)是可变的;

a[0] = 10
# 这个是在改对象,不会改变变量的指向,只是指向的变量的值变了;
a = [4,5,6]
# 这个是在改变量,会改变变量指向的对象;

列表

  • 列表就是存储对象的对象;

    #列表的切片;
    stus[-1] #从倒数第一个开始;
    stus[0:2] #第一个元素和第二个元素;左闭右开;
    stus[0:5:-1] #[起始:结束:步长],if步长是负数表示从后开始;步长不能是0;
    s.index[‘a’,1,3] #获取a在列表中在[1,3)中首次出现的索引;
    s.count[‘a’] #获取a在列表中出现的次数; 这两个都是方法,必须通过xxx.method()来调用;

    列表的替换

    在给切片进行赋值时,只能使用序列

    stus[0:2] = [1,2,3] # 序列的个数可以和切片的个数不一致;
    stus[::2] = [1,2] #当设置了步长时,两者个数必须相同;

  • 列表的方法

    s.append(1) # 向列表最后添加元素;
    s.insert(2,’a’) #向列表的2索引处插入a;
    s.extend([1,2,3]) #参数必须是序列,扩展s序列;
    s.pop(2) #删除索引为2的元素,有返回值;
    s.remove(2) #删除值为2的元素,if有多个值,只删第一个;
    s.clear() #清空序列;
    s.reverse() #反转列表;
    s.sort() #对列表排序,默认是升序,可以传递参数reverse=true来变成降序;

元组(tuple)

  • 元祖就是不可变的列表

    t = () # 创建空元组;
    t = 1,2,3,4 # 当元组不为空时,括号可以不写;
    t = 1, # if元组不为空,至少要有一个逗号;
    a,b,c,d = t # 元组的解包,这时候a=1,b=2;

    所以if想要交换两个元素,其实可以直接

    a,b = b,a # b,a就是一个元组,进行了解包;

    在对一个元组进行解包时,变量的数量必须和元组中的元素的数量一致

    也可以在变量前边添加一个*,这样变量将会获取元组中所有剩余的元素

    a , b , *c = t # c = [3,4];

    解包是对所有序列都可以的,不仅是元组;

字典(dict)

  • 列表存储数据很好,但是查询的性能很差;而字典从查询效率很高;

  • 字典中每个对象都有一个名字(key),而每个对象就是value;这样一个键值对称为一项(item);

  • 字典的值可以是任意对象;字典的键可以是任意的不可变对象(int、str、bool、tuple …),但是一般我们都会使用str

    d = {} #空字典
    d = {‘name’:a, ‘age’:18}

    使用 dict()函数来创建字典

    每一个参数都是一个键值对,参数名就是键,参数名就是值(这种方式创建的字典,key都是字符串)

    d = dict(name=’孙悟空’,age=18,gender=’男’)
    len(d) #获取字典中键值对的个数;

    in/not in #字典中是否有指定的键;

    d[‘name’] #获取字典的值;

    d.get(‘hello’,default) # if hello不存在,会返回默认值;

    d[‘name’] = ‘sunwukong’ # 修改字典的key-value
    setdefault(key, default) 可以用来向字典中添加key-value

    如果key已经存在于字典中,则返回key的值,不会对字典做任何操作

    如果key不存在,则向字典中添加这个key,并设置value

    d = {‘a’:1,’b’:2,’c’:3}
    d2 = {‘d’:4,’e’:5,’f’:6, ‘a’:7}

    d.update(d2) #将d2中的键值对添加到d,if 有相同的key会进行替代;

    del d[‘name’] # 删除键值对;
    result = d.pop(‘name’) #孙悟空,返回value;

    d.clear() # 清空字典;

    d1 = d2 # 两个指向的是同一个对象,修改一个另一个也会变;
    d1 = d2.copy # 这是复制品,是相互独立;

    注意,浅复制会简单复制对象内部的值,如果值也是一个可变对象,这个可变对象不会被复制
    d = {‘a’:{‘name’:’孙悟空’,’age’:18},’b’:2,’c’:3}
    d2 = d.copy()

    d2[‘a’][‘name’] = ‘猪八戒’ # d和d2都会变成猪八戒;

    for k in d.keys():

      print(d[k])    #通过keys()遍历;
    

    for v in d.values():

      print(v)       #通过values()遍历:
    

    for k,v in d.items():

      print(k, '=', v)  #items()会返回字典中所有的项;   
    

    会返回一个序列,序列中包含双值子序列,[(‘name’,a),(‘age’,18)],这时候赋值其实是解包;

集合(set)

  • 集合和列表非常相似

  • 不同点:
    1.集合中只能存储不可变对象 ( s= {[1,2,3]},这就是错误的,因为列表是可变的)
    2.集合中存储的对象是无序(不是按照元素的插入顺序保存)
    3.集合中不能出现重复的元素

    s = {1,2,3} #创建集合;
    s = set([1,2,1]) # set可以将列表和字典变为集合,字典转的时候,只有键;
    len(s) # 元素数量;

    in/not in

    s.add(4) # 增加元素;

    s1.upadte(s2) # 将s2中的元素添加到s1中;

    s.pop() # 随机删除集合中的一个元素;
    s.remove # 删除集合中的指定元素;

    s.clear()

    s1 & s2 # 集合的交集;
    s1 | s2 # 集合的并集;
    s1 - s2 # 集合的差集;
    s1 ^ s2 # 集合的异或集:获取只在一个集合中出现的元素;
    s1 <= s2 # s1 是否是s2的子集
    s1 < s2 # s1 是否是s2的真子集

函数

  • 函数也是对象;函数可以用来保存一些可执行的代码,并且可以在需要时,对这些语句进行多次的调用

    def fn(a,b):

      代码块    #fn是函数对象,fn()调用函数;
    

    函数在调用时,解析器不会检查实参的类型;实参可以传递任意类型;比如列表甚至是函数都行(fn(fn1));

    def fn1(*a,b):

      代码块  #可变参数,会将所有的实参保存到一个元组中(装包)
      # 可变参数不是必须写在最后,但是注意,带*的参数后的所有参数,必须以关键字参数的形式传递
    

    *形参不能接收关键字参数;

    形参可以接收关键字参数,会将这些参数统一保存到一个字典中;参数必须在所有参数的最后;


    t = (1,2)
    fn(*t)
    #传递实参时,可以在序列类型的参数前添加星号,这样他会自动将序列中的元素依次作为参数传递(解包),序列的个数必须和参数个数相同;

    同样,也可以用**来对字典进行解包,字典的key和参数形参名一样;


    return 后面可以跟任意的值,甚至可以是函数;

    文档字符串

    def fn(a:int,b:bool,c:str=’hello’) -> int:

      #这些里面的类型没有任何意义,仅仅是指示说明;
      ···
      可以写一些说明文字
      ···
    

    if 想在函数内部修改全局变量的值,要加关键字 global;
    scope = locals() # locals() 获取当前作用域的命名空间;

    命名空间其实就是一个字典,是一个专门用来存储变量的字典;所以scope是一个字典,这个字典就是当前作用域中所有的变量;

    scope[‘c’] = 1000 # 向字典中添加键值对就相当于在全局中创建了一个变量c;
    globals_scope = globals() #globals()这个函数可以在任意位置获取全局的命名空间;

高阶函数

  • 将函数作为参数,或者将函数作为返回值的函数是高阶函数;当将一个函数作为参数时,实际上是将指定的代码传递给了目标函数;
  • 这样的会就可以定制一些函数,然后将函数作为一种“规则”传递给目标函数,然后目标函数根据这种“规则”对参数(原料)做出相应的处理;

1.将函数作为参数传递:

filter()

filter()可以从序列中过滤出符合条件的元素,保存到一个新的序列中

  • 参数:
    1.函数,根据该函数来过滤序列(可迭代的结构)
    2.需要过滤的序列(可迭代的结构)
  • 返回值:
    过滤后的新序列(可迭代的结构)
匿名函数(lambda函数表达式)

有时候一个函数用一次就再也不用了,就可以用lambda表达式;匿名韩式一般都是作为参数使用的;
语法:lambda 参数列表 : 返回值

r = filter(lambda i : i > 5 , l)

2.将函数作为返回值

闭包
  • 将函数作为返回值返回,这就是一种闭包,通过闭包可以创建一些只有当前函数能访问的变量,可以将一些私有的数据藏到闭包里;

    形成闭包的条件:
    1.函数嵌套;
    2.将内部函数作为返回值返回;
    3.内部函数必须要使用到外部函数的变量;

    def fn():

      a = 10
    
      # 函数内部再定义一个函数
      def inner():
          print('我是fn2' , a)
    
      # 将内部函数 inner作为返回值返回   
      return inner
    

    r是一个函数,是调用fn()后返回的函数

    这个函数实在fn()内部定义,并不是全局函数

    所以这个函数总是能访问到fn()函数内的变量

    r = fn()
    变量是里面能看到外面的,但是外面看不到里面的;这样闭包就可以了;

装饰器

在写程序的时候,if我们想扩展一个函数,但是我们要尽量不去动原始的函数,而是要遵从开闭原则,即开放对程序的扩展,而关闭对程序的修改;

装饰器其实就是一个函数,这个函数什么功能呢?它接收一个旧函数作为参数,然后在装饰器里对它进行装饰包装,然后以一个新函数作为返回;这样就可以在不修改原始函数的情况下对函数进行扩展;

可以直接在旧函数上声明@装饰器函数
可以给函数多个装饰器,装饰的时候从内向外;

对象

  • 类用大驼峰来命名:MyClass; 类也是对象,类就是一个用来创建对象的对象,一切皆对象!

  • 如果是函数调用,则调用时传几个参数,就会有几个实参;但是如果是方法调用,默认传递一个参数,这个实参是解析器自动传的,所以方法中至少要定义一个形参(self),这个默认传递的实参其实就是调用方法的对象本身!if是p1调的,那第一个参数就是p1,if是p2调的,那第一个参数就是p2;所以我们把这个参数命名为self,这个self就是指的是谁调用这个方法了,这个谁就是self;self就是当前对象

  • 当我们调用一个对象的属性时,解析器会先在当前对象中寻找是否含有该属性,
    如果有,则直接返回当前的对象的属性值,
    如果没有,则去当前对象的类对象中去寻找,如果有则返回类对象的属性值,如果类对象中依然没有,则报错!

  • 类对象和实例对象中都可以保存属性(方法)
    - 如果这个属性(方法)是所有的实例共享的,则应该将其保存到类对象中
    - 如果这个属性(方法)是某个实例独有,则应该保存到实例对象中

  • 在python中,xxx 称为特殊方法,或者称为魔术方法;这种方法不需要我们自己调用,会在一些时刻自动调用;

    p1 = Person()的运行流程

    1.创建一个变量p1
    2.在内存中创建一个新变量,这个变量的类型是Person
    3.__init__(self)方法执行
    4.将对象的id(地址)赋值给变量
    
  • 使用__开头的属性,实际上依然可以在外部访问,所以这种方式我们一般不用,(因为其实就是python将__name自己改名成了_类名__属性)
    一般我们会将一些私有属性(不希望被外部访问的属性)以_开头,一般情况下,使用_开头的属性都是私有属性,没有特殊需要不要修改私有属性

  • 继承,在类的括号里写父类,if没写,就是继承自object.

    issubclass(a,b) 检查a是否是b的子类
    isinstance(a,A) 检查a是否是A的实例

父类中所有的方法和属性都可以被继承,包括特殊方法__init__,可以重写初始化方法,if想要增添多个属性,那可以调用super()来调用当前类的父类,然后再增添自己特有的就行了

# 父类中的所有方法都会被子类继承,包括特殊方法,也可以重写特殊方法
class Dog(Animal):

    def __init__(self,name,age):
        # 希望可以直接调用父类的__init__来初始化父类中定义的属性
        # super() 可以用来获取当前类的父类,
        #   并且通过super()返回对象调用父类方法时,不需要传递self
        super().__init__(name)
        self._age = age

python中可以多重继承:

class C(A,B):
    pass  
# C继承自两个父类,AB,ifA,B中有同名的方法,会先调用A的,
  • 多态:在函数的参数中,if要传入一个对象,其实不关注对象是什么类型的,只关注这个对象是否有某些属性和方法,只要有某些属性和方法,那就可以传递进去,这样保证了程序的灵活性;

  • 类属性可以通过A.属性和a.属性访问,实例属性只能通过a.属性访问,类.属性无法访问;在类中以self作为第一个参数的方法是实例方法,实例方法可以通过类和实例调用,通过实例调用时,会自动将当前对象传递给self,但是通过类调用时,不会自动传,所有,A.fangfa(a) = a.fangfa()

      # 类方法    
      # 在类内部使用 @classmethod 来修饰的方法属于类方法
      # 类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象
      #   类方法和实例方法的区别,实例方法的第一个参数是self,而类方法的第一个参数是cls
      #   类方法可以通过类去调用,也可以通过实例调用,没有区别
      @classmethod
      def test_2(cls):
          print('这是test_2方法,他是一个类方法~~~ ',cls)
          print(cls.count)
          
      # 静态方法
      # 在类中使用 @staticmethod 来修饰的方法属于静态方法  
      # 静态方法不需要指定任何的默认参数,静态方法可以通过类和实例去调用  
      # 静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数
      # 静态方法一般都是一些工具方法,和当前类无关
      @staticmethod
      def test_3():
          print('test_3执行了~~~')
    
  • 特殊方法也叫魔法方法,一般不需要手动调用,在一些特殊的时候会自动执行,以__开始,比如__init__在对象创建时调用,__del__会在结束时调用,进行垃圾回收

  • 一个py文件就是一个模块,模块也是一个对象!在每一个模块内部都有一个__name__属性,通过这个属性可以获取到模块的名字__name__属性值为 __main__的模块是主模块,一个程序中只会有一个主模块,主模块就是我们直接通过 python 执行的模块

  • 一个文件夹就是一个包,一个包里可以有多个模块,包中必须含有一个__init__.py文件,一个包里会有__pycache__文件夹,这是模块的缓存文件, py代码在执行前,需要被解析器先转换为机器码,然后再执行,所以我们在使用模块(包)时,也需要将模块的代码先转换为机器码然后再交由计算机执行
    而为了提高程序运行的性能,python会在编译过一次以后,将代码保存到一个缓存文件中,这样在下次加载这个模块(包)时,就可以不再重新编译而是直接加载缓存中编译好的代码即可

异常

    try:
        代码块(可能出现错误的语句)
    except 异常类型 as 异常名:
        代码块(出现错误以后的处理方式)
    else:
        代码块(没出错时要执行的语句))
    finally:
        代码块(该代码块总会执行) 
  • 异常也是一个对象,比如 : ZeroDivisionError类的对象专门用来表示除0的异常
    NameError类的对象专门用来处理变量错误的异常
  • Exception 是所有异常类的父类,所以如果except后跟的是Exception,他也会捕获到所有的异常;可以在异常类后边跟着一个 as xx 此时xx就是异常对象
  • 可以使用 raise 语句来抛出异常,
    raise语句后需要跟一个异常类 或 异常的实例

文件

  • 打开文件,opne(file_name)会返回一个对象,if当前文件和目标文件在同一级目录下,则直接写名字就可以,其他的时候就必须用路径了。可以使用..来向上返回一级目录

  • read()用来读,会将内容保存为一个字符串返回;

  • 关闭文件;对象.close(),这时候其实经常是忘记的,所以有了with..as语句

    with open(file_name) as file_obj: #这其实就是和file_obj = open(file_name)一样,当出去这个with后,会自动关闭;
    #打开file_name并返回一个变量file_obj

      file_obj.read()   
    

    with open(file_name , ‘x’ , encoding=’utf-8’) as file_obj:

    使用open()打开文件时必须要指定打开文件所要做的操作(读、写、追加)

    如果不指定操作类型,则默认是 读取文件 , 而读取文件时是不能向文件中写入的

    r 表示只读的

    w 表示是可写的,使用w来写入文件时,如果文件不存在会创建文件,如果文件存在则会截断文件

    截断文件指删除原来文件中的所有内容

    a 表示追加内容,如果文件不存在会创建文件,如果文件存在则会向文件中追加内容

    x 用来新建文件,如果文件不存在则创建,存在则报错

    + 为操作符增加功能

    r+ 即可读又可写,文件不存在会报错

    seek() 可以修改当前读取的位置

      seek()需要两个参数
      第一个 是要切换到的位置
           第二个 计算位置方式
              可选值:
               0 从头计算,默认值
               1 从当前位置计算
               2 从最后位置开始计算
    

    tell() 方法用来查看当前读取的位置