基础

  • 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}
print(d['name]')   # a;注意name一定要加引号;
----------------------
# 使用 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() 方法用来查看当前读取的位置


04-01 01:01