python学习笔记

if-else简化版:
表达式1  if 表达式  else 表达式2
y = 5
x = "大于0" if y > 0 else "小于0"
print(x)
输入数值,判断是工作日还是休息日,或者输入错误。
day = int(input("请输入数值:"))
content = "工作日" if 1 <= day <= 5 else ("休息日" if day == 6 or day == 7 else "输入错误")

# 断言的使用assert
# 断言的语法:
# assert 表达式
# assert 表达式  信息
# 当表达式为True,则流程继续进行,否则会产生错误。
# 当指定信息时,如果断言的表达式为False,则信息也会显示。

# 断言与if的不同之处
# 1断言应该使用在程序的关键位置。if往往使用在不是特别关键的位置。
# 2断言可以通过运行时参数-O,来灵活的开启或者关闭(默认是开启的)。但是,if总是无条件的执行。

password = input("请输入密码:")
password2 = input("请再次输入密码:")
# 此处设下断言,仅当两次密码一致时,才有必要进行后续的注册工作。
# assert password == password2
assert password == password2, "两次密码输入的不一致"

# range函数  能够产生一个区间的整数值(可迭代对象)。
# range(end)    产生0 ~ end区间的整数值。包括0,不包括end。[0, end)
# range(start, end) 产生start ~ end 区间的整数值。包括0,不包括end。[start, end)
# range(start, end, step) step指定步长值(增量值)。step默认为1。

 

#列表的索引也支持负值。-1, -2,表示倒数第几个元素。最后一个元素为-1。
#print(li[-1], li[-2])
# 列表中的元素可以不是同一个类型。
li2 = [1, 2.0, False, "abc"]
# 输出整个列表。
print(li2)
# 通过for循环遍历列表,比while循环更加简洁。
for item in li:
    print(item)
# 列表的嵌套:列表中的元素,还是一个列表。
nest = [[1, 2, 3], [4, 5, 6]]
for item in nest:
    for i in item:
        print(i)
# # 列表(序列)的切片操作
# # 语法:列表名[start:end:step]
# # start指定切片起始点,end指定切片的终止点,step指定步长值,默认为1。(切片区间包含起始点,不包含终止点)
# # start,end,step都是可选的。
li = [5, 3, 8, 6, 10, 9, -2]
print(li[0:3])
# # 步长指定增量值。
print(li[0:6:2])
# # start, end, step也可以是负值。
# # 如果切片的方向与增量的方向相反,则无法获取任何元素。(得到空列表[])
print(li[-1:-4])
print(li[-1:-4:-1])
print(li[-4:-1])
# # 省略start或end
# # 如果缺失起始点或终止点(或者两个都缺失),则此时切片没有方向。具体沿着哪个方向切取,取决于
# # 增量的方向。
print(li[:4])
print(li[4:])
print(li[:4:-1])
print(li[4::-1])
print(li[::1])
# # 通过切片实现对原列表的逆序。
print(li[::-1])
#
# #通过切片修改对应区间的元素。
# # 因为列表切片返回的是列表类型,因此,赋值时,需要指定一个列表
print(type(li[2:5]))
li[2:5] = [100, 101, 102, 103]  # 相当于替换操作。
# # 相当于删除列表中指定区间的元素。
li[2:5] = []
# # 相当于插入元素。
li[2:2] = [32, 50]
print(li)
#
# # 对于索引,如果越界,将会产生错误。
# # print(li[len(li)])
# # 对于切片,如果越界,不会产生错误。
# print(li[-len(li)-1: 3])
# print(li[:len(li) + 100])
# print(li[-10000:10000])
# list(列表)的copy方法实现的是浅拷贝。浅拷贝仅仅拷贝当前的对象,对于对象内部所关联的其他对象,
# 则不会进行拷贝。
# 列表中元素是可变类型,此时,浅拷贝无法实现期望的效果。如果期望li2的改变不会影响到li,我们可以实现
# 深拷贝操作。深拷贝就是不仅拷贝当前对象本身,当前对象内部所引用的可变对象,依然会实现递归的拷贝。
# id函数,返回对象的唯一标识。不同的对象,id函数一定会返回不同的值。
# 在CPython中,id函数返回的是对象的地址。

 

# 元组使用()来定义。
t = (1, 2, 3)
print(type(t))
# 当只有一个元素时,元素后面的逗号(,)不能省略。
# 否则()会被解析为调节运算符优先级的(),而不会解析为元组类型。
# 定义元组时,也可以不使用小括号,只要使用逗号分隔即可。
k = 1,
k = (1, 2)
s = (1, 2)
print(k is k)
print(k is s)
print(k == s)
# 将当前字符串中,第1个参数指定的内容,使用第2个参数进行替换。
# print(s.replace("cd", "xy"))
# replace也可以指定第3个参数,表示最多替换的次数。
# print(s.replace("cd", "xy", 3))
# 用来拼接可迭代对象中的所有元素,使用当前str对象作为分隔符。
li = ["a", "bc", "d", "ef"]
print("-".join(li))
# 去除字符串两端的空白符。
print("\t\t\t    abcc\t\t\t".strip())
# strip方法也可以指定我们需要去掉的字符。
# strip方法参数指定的是单个字符,并不是作为一个整体。即strip两端的字符只要在参数指定的内容当中,则去除。
print("xyzxyzxyxy".strip("yx"))
# 以空白符进行切割,返回切割后元素组成的列表。
print("ab cd ef gh".split())
# split也可以指定切割的字符。
print("abcdefcabckk".split("c"))
当迭代的对象迭代完并为空时,位于else的子句将执行,而如果在for循环中含有break时则直接终止循环,并不会执行else子句:
s = "01234a58789"
for item in s:
    if 48 <= ord(item) <= 57:
        continue
    else:
        break
else:

 

字典:
# 字典的键与值之间使用:进行分隔。
# 键值对之间使用,进行分隔。
x = {1: 200, 2: 300, 3: 400}
# 字典中的键是不能重复的。
# 字典中的键是无序的。(不保证任何顺序)
# get方法与x[key]的区别:二者都能够获取键所绑定的值,不同之处在于:
# 前者在键不存在时,不会产生错误,返回None,而后者会产生错误。
print(x.get(1))
print(x[1])
# pop:删除与参数指定键相同的键值对。同时,返回该键所绑定的值。
# 如果指定的键不存在,则会产生错误。
# print(x.pop(1))
# pop可以指定第2个参数(默认值)。如果第1个参数指定的key不存在,则不会
# 产生错误,而是返回第2个参数。
# print(x.pop(4, 2))
# print(x)
# 返回字典中所有的键key。
# print(x.keys())
# 返回字典中所有的值value。
# print(x.values())
# 返回字典中所有的键值对。每个键值对为一个元组。(索引0为key,索引1为值。
# print(x.items())
# 删除字典中所有的键值对。
# x.clear()
# 使用参数指定的字典,更新当前的字典。
# 相当于参数y中的每一个k与v,执行x[k] = v。
y = {3:89, 4:2323, 5:"csdf"}
x.update(y)
# 字典的遍历
for k in x.keys():
    print(k, x[k])
# for t in x.items():
#     #print(t)
for a, b in x.items():
    print(a, b)
集合:
# 如果想要创建空的集合。使用set函数。
x = set()
# 集合的特征:
# 1 集合中不含有重复元素。
x = {1, 2, 3, 1, 2, 3}
print(x)
# len也可以应用与字典与集合类型。
# 对于字典类型,返回键值对的数量。对于集合类型,返回集合中元素的数量。
print(len(x))
print(len(d))
# 2 集合中的元素不保证任何顺序。(无序)
# 3 集合中元素的类型必须是可哈希类型。
# x = {[1, 2, 3], 4}
# print(x)

# 集合与字典具有很多共同的特征,这些不是偶然的。因为,集合底层就是使用字典来实现的。
# 集合中的元素就会成为字典的key,然后绑定一个固定的值。
x = {1, 2, 3}
# x = {1:None, 2:None, 3:None}
# 在Python中,任何类型都可以转换成布尔类型。

格式化:
1.旧格式
# # %d 将显示的值转换成有符号十进制数字
# print("我在%d年,我%d岁。" % (year, age))
# 将显示的值使用str函数转换成字符串。
print("%s" % "30")
# 使用关键字
x = {"name": "xy", "age": 22}
# print("姓名:%s,年龄:%d" % ("xy", 22))
# 指定字典中的键,提取该键在字典中对应的值。
print("姓名:%(name)s,年龄:%(age)d" %x)
2.新格式:使用str的format方法进行格式化
{[字段名][转化类型]【格式说明】}
使用{}作为占位符
print("{1}-{0}".format("后面的","前面的"))
指定关键字,则按照后面同名的关键字参数的值进行替换。
print("{back}-{front}".format(back="ba",front="fr"))
使用数字(关键字)的索引进行替换
li=[1,2,3]
print("0[1]".format(li))
格式说明:
[[填充符]对齐方式][符号][#][0][最小宽度][分组选项][.精度][转换类型]
print("{*>10}".format(-123))
3.最新格式化  {[字段名][!转换类型][:格式说明]}
格式化字符串常量,使用f或F前缀
x=200
print(f"{x}")

 

 

# 函数的概念:
# 函数是具有一定功能的代码片段(语句体),该代码片段能够重复的执行。
# 函数的定义,使用def关键字。
# def 函数名(参数列表):
#       函数体
只执行到return,return 后面的不执行
# 函数返回多个值,不能使用多个return,因为函数遇到return就会终止执行,后面的return无法返回值。
# 我们可以使用元组来返回多个值。(尽管列表也可以,但是元组更加合适,因为元组不能修改。)
def compute(a, b):
    # return a + b
    # return a - b
    # return a * b
    # return a / b
    # return [a + b, a - b, a * b, a / b]
    return (a + b, a - b, a * b, a / b)

# print(compute(1, 2))
#li = compute(1, 2)
#print(li[1])

# 如果一个参数含有默认值,则该参数就成为一个可选参数(可有可无)。
# 如果我们显式传递了该参数,则该参数的值就是我们传递的值。如果我们没有显式
# 传递该参数,则该参数取默认值。
def print_star(line, char="*"):
    for i in range(line):
        print(char * line)
# # 含有默认值参数位置的要求:
# # 在定义函数时,含有默认值的参数一定要定义在没有默认值参数的后面。

# 位置参数必须位于关键字参数之前。
 关键字可变参数,在参数前面加上**。
# 关键字可变参数,可以用来接收任意数量的关键字参数。
# 在函数调用过程中,会执行打包操作。关键字参数会打包成字典类型。
def p(**k):
    # print(type(kw))
    print(k)

p()
p(a=2, b="asdf", c=[1,2,3,4])
 # 位置可变参数,在参数前面加上*。
# # 位置可变参数可以用来接收任意数量的位置参数。
# # 在函数调用过程中,会执行打包操作。位置可变参数是一个元组类型。
# def s(*a):
#     # print(type(a))
#     sum = 0
#     for item in a:
#         sum += item ** 2
#     return sum
#
#
# print(s())
# print(s(1))
# print(s(1, 2))
# print(s(1, 2, 3, 4, 5))
#位置可变参数与关键字可变参数如果存在,则最多只能定义一个。
def test4(*a, *b, **kw, **kw2):
    pass
test4(1, 2, 3, 4)
# # 在可变位置参数后,定义的位置参数,将会自动的成为关键字参数。
# def test3(*a,  b):
#     pass
#
# test3(1, 2, 3, 4, 5, b=3)
# 可变参数用来接收的是未匹配的参数,如果参数已经匹配,则不会由可变参数接收。
# def test(a, *b):
#     print(a)
#     print(b)
#
# test(1, 2, 3)



# #参数传递
# def fun(a):
#     a = 100
#
# b = 20
# fun(b)
# print(b)


def fun2(a):
    a.append(100)

li = [1, 2, 3]
fun2(li)
print(li)

def fun3(a):
    a = []

li = [1, 2, 3]
fun3(li)
print(li)

# 结论:通过参数传递,我们可以使形参与实参绑定相同的对象,进而,通过形参可以改变实参所绑定对象的数据。
# 但是,不能通过改变形参所绑定的对象,进而影响实参(实参所绑定的对象不会进行更改。即在函数调用之前,实参
#绑定哪个对象,在函数调用之后,实参依然会绑定原来的那个对象。

 

__init__方法:
既然__init__方法具有在创建对象时自动执行的特征,我们就可以在该方法中执行对象的初始化,定义属性就是一件最常用的操作。而__init__方法具有初始化的能力,我们也习惯将其称为构造器或构造方法。
与类的对象相关联的,这种属性称为实例属性。对于对象,也称为实例,例如,创建类的对象,也可以称为,创建类的实例。
类属性既可以通过类访问,也可以通过对象访问(类属性会共享给所有对象)。
当读取属性值时,首先尝试访问同名的实例属性,如果实例属性不存在,则会访问类属性(如果类属性也不存在则会产生错误)。
当修改属性值时,如果实例属性存在,则修改,如果实例属性不存在,则新增该实例属性,即通过对象修改属性时,操作的属性一定是实例属性。
对于面向过程而言,体现的是一种流程设计,即按部就班的完成每一个环节。为了实现每个环节的可复用性,环节都是通过调用一个个函数来实现的。因此,在面向过程的编程中,通常都是定义大量的函数,按照需求顺序依次进行调用。
面向对象与面向过程:
    而面向对象程序设计则不同,在面向对象中,会根据需求,划分为若干个类,每个类会创建对象,以对象的方式来进行交互。所以,在面向对象中,我们的程序不再是以函数为单位,而是以对象为单位。
    # 面向过程编程中,程序中充满大量的函数。
    # 面向对象编程中,程序中通过对象,调用对象提供的方法处理。
# 类的定义
# 使用class关键字
# 类中可以定义属性与行为。
# 属性通过定义变量来表示。行为通过方法来表示。
# 其实,方法就是函数,只不过函数是面向过程的称呼,而方法是面向对象的称呼。
# 函数不依赖与一个类的对象,而方法依赖与一个类的对象。
对象的初始化:
    # 我们定义__init__方法时,可以为该方法提供一些参数,来更加灵活的进行初始化,这样可以
    # 避免千篇一律的创建对象。
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 建议:总是通过类名来访问类属性,而不要通过对象访问类属性,因为这会造成不必要的混淆。

# 类属性与实例属性,二者名称相同也没有问题。
# 通过类名访问的,永远是类属性。通过对象进行访问的,可能是类属性,也可能是实例属性。

    # 类方法,使用@classmethod修饰
    # 类方法的第一个参数是固定的,根据惯例,我们将其命名为cls(class的简称),
    # 但这不是必须的。
    @classmethod
    def cm(cls):
        print("这是类方法")
        print(cls is Person)

在一个类中操作另一个类的属性的方法见第二个习题

静态方法使用@staticmethod修饰
    # 可以看出,静态方法既没有self,也没有cls。所以,静态方法设计的原则:既不访问当前的类属性,也不访问
    # 当前对象的实例属性。
    # 从结构上讲,我们完全可以将静态方法移除到类之外,静态方法就真的像函数一样。
    # 但是,静态方法定义在类的内部,还是具有一定的意义。因为有些方法就是完全为一个类而服务的,处于逻辑上
    # 的关联性,我们就可以将该方法定义在类之中,成为静态方法。

 

面向对象具有三大特征,分别为:
Ø封装
Ø继承
Ø多态
在程序中,我们可以通过将变量私有化来做到封装。所谓的变量私有化,就是在类中定义的变量,仅能在当前类(定义变量的类)中访问,而不能在类的外部访问。
如果一个属性名(或方法名)使用两个下划线(__)开头,并且少于两个下划线结尾,则这样的属性(方法)就称为私有属性(方法)。私有属性(方法)只能在类的内部访问。
名称的私有化,这会给客户端带来一些问题。比如,客户端想要获取Person对象的名字(__name属性),或者设置该属性的值,现在都已经无法做到。

为了能够不影响客户端的正常访问,我们可以提供公有的访问方法,一个用来获取私有属性值,一个用来设置私有属性值。

在Python语言中,所谓的私有,不过是一种假象。当我们在类中定义私有成员时,在程序内部会将其处理成_类名 + 原有成员名称的形式。也就是会将私有成员的名字进行一下伪装而已,如果我们使用处理之后的名字,还是能够进行访问的。

在客户端访问时,公有的方法总不如变量访问那样简便,怎样才能既可以直接访问变量,又能够实现很好的封装,做到信息隐藏呢?
我们可以使用property的两种方式来实现封装:
Ø使用property函数
Ø使用@property装饰器

继承的语法为:
class B(A):
    类体

因为实例属性是定义__init__方法中,实现与对象(self)的绑定。如果子类没有定义init方法,就会继承父类的init方法,从而在创建对象时,调用父类的init方法,会将子类对象传递到父类的init方法中,
从而实现子类对象与父类init方法中实例属性的绑定。但是,如果子类也定义了自己的init方法(重写),则父类init方法就不会得到调用,这样,父类init方法中定义的实例属性就不会绑定到子类对象中。

虽然父类的__init__不完全适合子类,但是也并非完全不适合子类。因为两个类还是存在相同的属性的,因此,我们应该充分利用现有的功能,不要重复的实现。
实现方式就是,我们在子类的构造器中,去调用父类的构造器,完成公共属性的初始化,然后在子类构造器中,再对子类新增属性进行初始化。我们可以这样来调用父类的构造器:
super().__init__(父类构造器参数)

当子类继承了父类,子类就可以继承父类的成员。然而,父类的成员未必完全适合于子类(例如鸟会飞,但是鸵鸟不会飞),此时,子类就将父类中的成员进行调整,以实现适合子类的特征与功能。我们将父类中的成员在子类中重新定义的现象,称为重写。

# 使用property可以定义一个属性(伪装一个属性)。
# 通过property既可以提供访问的便利性,同时也能够提供很好的封装性。
# 定义property有两种形式:
# 1 使用property函数。
# 2 使用@property装饰器
class Computer:
    def __init__(self):
        self.__memory = 1024

    # property的4个参数:
    # 1 get方法 在读取property值的时候,会调用该方法。
    # 2 set方法 在修改property值的时候,会调用该方法。
    # 3 del方法 在删除property值的时候,会调用该方法。
    # 4 doc 给出该property的说明文档。
    def get_memory(self):
        print("get方法得到调用")
        return self.__memory

    def set_memory(self, memory):
        print("set方法得到调用")
        self.__memory = memory

    def del_memory(self):
        print("del方法得到调用")
        del self.__memory

    memory = property(get_memory, set_memory, del_memory, "电脑的内存大小")
    # 如果不提供第2,3个参数,则该属性就是只读属性了。
    #memory = property(get_memory)

c = Computer()
print(c.memory)
c.memory = 2000
del c.memory
help(Computer.memory)

# 子类可以继承父类中定义的类属性,实例属性,类方法,实例方法,静态方法。

又继承又增加:
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
class Student(Person):
    def __init__(self, name, age, id):
        #调用父类的__init__方法
        super().__init__(name, age)
        self.id = id

# 关于私有成员的继承
# 实际上,私有成员也是可以被子类继承的。只不过,子类继承的不是私有成员本来的名字,
# 而是私有成员伪装之后的名字。
class Father:
    # _Father__m
    def __m(self):
        print("私有方法")


class Son(Father):
    def p(self):
        # 错误
        #self.__m()
        # 正确,通过伪装之后的名字访问。
        self._Father__m()

 

# 模块
# 模块就是一个文件,只是文件是从物理角度来讲的,而模块是从逻辑角度来讲的。

# 定义模块的优势(好处):
# 1 定义模块有利于将项目按照功能进行划分,每个人负责一个或多个部分(模块),
# 从而能够进行协作式开发。
# 2 模块提供了独立的命名空间(全局命名空间),能够命名冲突。
# 3 模块可以供多人使用,提高了程序的复用性。

# 模块的别名:
# 使用as可以为模块或者模块内的成员名称取一个别名。
# 别名的好处:
# 1 别名可以有效的解决命名冲突。
# 2 当名称特别长时,我们可以取一个相对简短的别名,减少输入量。

# 返回a(第1个参数) - b(第2个参数)之间的随机整数,[a, b](包括两个端点)。
# print(random.randint(1, 3))

# 返回a(第1个参数) - b(第2个参数)之间的随机整数,[a, b)(包括起始点,不包括终止点)。
# print(random.randrange(1, 3))

# 包类似于操作系统中的文件夹(目录,路径)。
# 包的作用:
# 1 包提供对模块的分类管理。
# 2 包提供独立的命名空间,可以解决模块的命名冲突。

time模块:
#time.localtime()将一个时间戳转换成当前时区的struct_time
print(time.localtime())
#time.gmtime()和time.localtime()类似,time.gmtime()是将一个时间戳转换为UTC时区(0时区)的strcut_time
print(time.gmtime())

#time.asctime()把一个表示时间的元组或者struct_time表示为这种形式: Wed Dec  5 14:58:17 2018
print(time.asctime())

#time.ctime()把一个时间戳(按秒计算的浮点数)转化为time.asctime()的形式
print(time.ctime())

#把一个代表时间的元组或者struct_time转化为格式化的时间字符串,如果t未指定,将传入time.localtime()
print(time.strftime("%a",time.localtime()))

#把一个格式化时间字符串转化为struct_time。实际上它和strfttime()是逆操作
time.strptime("2018-01-01",'%Y-%m-%d')

 

可迭代对象:从简单的角度讲,这些可以用在for循环中,进行遍历的对象,我们称其为可迭代对象。
生成器不会预先准备好所有的数据,而是在需要时,每次仅生成一个数据。这样,在处理大量数据时,也不会占用大量的内容空间。
闭包是指在函数体内部,访问到其外围函数中定义的变量。
装饰器,用来处理被其所装饰的函数,然后将该函数返回。从实现的角度讲,装饰器本身也是一个函数,其参数用来接收另外一个函数,然后返回一个函数,返回的函数与参数接收的函数可以相同,也可以不同。

# 生成器表达式非常类似于列表推导式,只是,将列表推导式的[]改成()。生成器表达式会返回一个生成器对象。
# 列表推导式
li = [i ** 2 for i in range(1, 101)]
print(type(li))
print(li)
# 生成器表达式表达式
li = (i ** 2 for i in range(1, 101))
print(type(li))
# 对于列表,我们可以看到列表中所有元素的值,但是对于生成器,我们看不到生成器元素的值。
# 因为生产器是一种惰性计算的方式。仅当我们请求时,才会计算一个数据。不会一次性计算所有的数据。
print(li)

# 我们可以通过for循环来获取生成器中的数据。
for item in li:
    print(item)

# 生成器函数与普通函数的区别:
# 1 对于普通函数,当调用函数时,就会执行函数体。对于生成器函数,当调用函数时,不会执行函数体,而是会
# 返回一个生成器对象。当我们调用生成器对象的__next__等方法时,才会执行函数体。
# 2 对于普通函数,每次调用,都会重新进行初始化。对于生成器函数,当调用__next__等方法执行时,遇到
# yield就会暂停执行,将yield后面的值返回给调用端(作为__next__等方法的返回值)。暂停执行时,生成器
# 函数内部的变量等状态都会得到保存,当再次调用生成器对象的__next__等方法时,会从刚才yield暂停的位置,
# 继续执行。

# 生成器函数与普通函数:就看函数体中有没有yield,如果存在yield,则为生成器函数,否则为普通函数。
# 生成作为特殊的迭代器,当迭代器无法进行迭代时(没有元素可迭代),会产生StopIteration异常,
# 因此,生成器也有这个特征。当生成器函数体执行完毕时,就会产生StopIteration异常。

# 当调用生成器对象的__next__方法时,生成器函数体就会得到执行。除此之外,当调用
# 生成器对象的send方法时,也会执行生成器的函数体。send方法的返回值也是yield所
# 产生的值。(这点与__next__是相同的。)

# __next__与send不同之处在于:send方法除了可以获取生成器对象产生的值,同时,
# 也可以向生成器对象传递值。传递的值将作为yield表达式的值。

# yield表达式的值取决于客户端调用的方法。如果客户端调用的是__next__方法,则
# yield表达式的值为None,如果客户端调用的是send方法,则yield表达式的值为send
# 方法传递的参数值。

# 闭包
# 闭包指的是内部函数引用(访问)外围函数中定义的变量,对于内部函数来说,就是闭包。
# 闭包发生在函数嵌套的场合中。

# 闭包的作用:
# 1 当内部函数执行结束时,内部函数所引用的外围函数中定义的变量状态(值)依然可以保留下来。
# 当下一次调用内部函数时,外围函数中的变量依然是内部函数上一次所离开时的值。
# 2 内部函数的名称处于局部命名空间中,这样就不会对外面的全局命名空间造成影响。即使名称相同也没关系。

# 装饰器本质上就是一个函数(或类),能够在不修改现有函数代码的情况下,对现有函数的功能实现扩充。
# 语法:@装饰器名  来修饰需要进行功能扩展的函数。

from datetime import datetime
# 函数经过装饰器装饰后,返回的函数已经不再是装饰之前的函数,因此,原函数的元信息(名字,说明文档,注解等)
# 都会丢失。

from functools import wraps


def check_in(fun):
    # 保留原函数的元信息
    # 功能:将wraps参数指定的函数的元信息复制给@wraps所修饰的函数。
    # 以本程序为例,将fun函数的元信息复制给inner函数。
    @wraps(fun)
    def inner(*args, **kwargs):
        print(datetime.now())
        return fun(*args, **kwargs)
    return inner



@check_in
# on_duty = check_in(on_duty)
def on_duty(name: str) -> None:
    """
    这是上班函数。
    """
    print(f"{name}上班了")


on_duty("Bill")
# 返回函数的名字
print(on_duty.__name__)
# 返回函数的说明文档
print(on_duty.__doc__)
# 返回函数的注解
print(on_duty.__annotations__)

# 离异常产生越近的信息,会在越下面显示。因此,我们以后分析异常时,应该从下向上进行查看。
常见的异常类型
# BaseException Python中所有异常类的根类。
# Exception BaseException的子类。用户异常的根类,我们自定义的异常应该继承该类。
# ZeroDivisionError 当0做除数时,会产生该异常
# print(5 / 0)
# AssertionError 断言的异常。当断言的条件不满足时,会产生该异常。
# assert 5 > 10
# ModuleNotFoundError 当使用import导入的模块不存在时,会产生该异常。
# import asdfadf
# IndexError 索引异常。当索引越界时,会产生该异常。
# li = [1, 2]
# print(li[2])
# KeyError 键访问异常。当键存在时(字典),会产生该异常。
# d = {"a": "sdfds"}
# print(d["b"])
# NameError 名称异常。当访问名称不存在时,会产生该异常。
# print(a)
# RecursionError 递归异常。当函数调用层次达到了最大层次限制时(通常但不绝对是递归),会产生该异常。
# def x():
#     x()
# x()
# StopIteration 终止迭代异常。当迭代器已经没有元素时,会产生该异常。
# li = [1]
# it = li.__iter__()
# it.__next__()
# it.__next__()
# SyntaxError 语法错误异常。当我们编写的代码不符合Python的语法规定时,会产生该异常。
# if 5 > 3:
# ValueError  值异常。当对值处理错误时,就会产生该异常。
# int("ab")

# 当异常产生时,异常之后的语句不会得到执行,这会影响程序的正常流程。

try中产生了异常,except能够处理该异常。
# try中产生异常,则此时会创建相关异常类型的对象,try中异常之后的语句将不会得到执行(不论except能否
# 捕获该异常)。然后,会使用该异常对象与各个except分支捕获的异常类型进行匹配。匹配原则:
# isinstance(异常对象, except捕获的异常类型),如果匹配成功(函数返回值为True),则执行对应的except
# 分支。匹配顺序:从上到下(最多只会执行一个except分支)。
# 如果能够匹配,则表示成功捕获异常(异常被消灭了),try-except之后的语句可以正常得到执行。

# 当进行多个except捕获异常时,如果多个except分支的类型存在继承关系,
# 我们需要将子类放在前面,父类放在后面。

# 在进行异常处理时,except分支可以不指定类型。
# 表示会处理所有try中产生的异常类型。
# 不指定异常类型的except必须作为最后一个except分支。并且,不指定异常类型的except只能具有一个。

# else 语句的作用:
# try中的语句应该是可能产生异常的语句,对于不会产生异常的语句,我们就不应该(不适合)
# 放入try中。else就可以将try中不产生异常的语句从try中分离,令程序结构变得更加清晰。

# 考虑到finally总是会执行的特征,我们可以在finally当中进行一些清理的工作,是非常合适的。

def register(age):
    if age <= 0:
        # 我们可以在不符合要求时,主动去产生一个异常,这样,就能够给客户端一个
        # 有效的提醒。因为客户端不明确处理,程序就会停止。(而if判断的方式,程序不会停止)
        raise ValueError(f"age值必须大于0,你给的值不正确:{age}")
    # else:
    print("执行注册的操作")

try:
    register(-3)
except ValueError:
    print("进行恢复措施!")


自定义异常:自己根据可能程序执行过程中可能出现的情况定义一个异常的处理方法,比如定义一个异常捕捉小于0的输入

 

 

os模块提供了很多操作目录与文件的功能。

# 以x模式操作文件,文件必须不存在,如果存在,则产生FileExistsError。

# 在with处打开的文件,可以保证,在with结束后一定能够得到有效的关闭。(无需我们显式调用close方法)
# with open("c:/a.txt"):
#     # 对文件进行的操作
#     pass
#
# #with中,使用as来保存文件对象(获得文件对象的引用)
# with open("c:/a.txt") as f:
#     # 可以通过f操作文件对象。
#     pass

# 当访问文件结束后,应该要断开与文件的链接。这样才能够让别人进行访问。
# f.close()

# # 因为readlines方法会一次性读取文件的全部内容,当文件内容非常大时,这非常耗费内容空间。
# # 因此readlines方法不适合操作大型文件。

# # 文件对象是迭代器类型。迭代器中的每个元素,就是文件的一行。
with open("d:/a.txt", "rt", encoding="UTF-8") as f:
    for line in f:
        print(line, end="")

# 对于w模式,如果文件不存在,则新建文件,如果文件存在,则覆盖原有文件。
# with open("c:/a.txt", "wt") as f:
#     # 向文件写入数据。
#     f.write("我想写入点内容,修改一下。")

# 对于a模式,如果不存在,则新建文件,如果文件存在,则追加写入。
# with open("c:/a.txt", "at") as f:
#     f.write("继续追加一点内容")

with open("c:/a.txt", "rt") as f, open("c:/cp.txt", "wt") as f2:
    # 适合与小文件
    # f2.writelines(f.readlines())
    for line in f:
        f2.write(line)

# 创建参数指定的路径。如果路径已经存在,或者路径的父路径不存在,则会产生错误。
# os.mkdir("c:/abc/def")
# 创建参数指定的路径。如果路径所在的父路径不存在,会连同父路径一同创建。
# 如果路径已经存在,则会产生错误。我们可以指定exist_ok参数为True(该参数默认为False),则存在
# 也没有问题。
# os.makedirs("c:/abc/def/ghi", exist_ok=True)

# 删除参数指定的目录,如果目录不存在,或者目录不为空,则会产生错误。
# os.rmdir("c:/abc/def/ghi")
# 删除参数指定的目录。子目录删除之后,会继续检查父目录,如果父目录此时也为空,则会将父目录一同删除。
# 直到根目录为止。
# os.removedirs("c:/abc/def/ghi")
# 删除参数指定的文件。如果文件不存在,则产生错误。
# os.remove("c:/abc/a.txt")

# 对文件进行重命名。第1个参数:源文件(目录)。第2个参数:修改之后的文件(目录)名。
# os.rename("c:/abc/1.txt", "c:/abc/2.txt")
# 与rename相似,只是源文件与目录文件的路径可以不一致(rename必须一致),此时就相当于是移动文件。
# 如果移动之后,父目录成了空目录,会将父目录删除。
# os.renames("c:/abc/2.txt", "c:/def/2.txt")

# 返回参数路径的绝对路径。
# print(os.path.abspath(".."))


# 复制参数指定的目录。包括目录中所有的子目录与文件。(递归复制)
# 第1个参数:源目录所在路径
# 第2个参数:目标目录所在的路径。(要求不能存在)
# 返回复制之后目录所在的路径。
# print(shutil.copytree("c:/abc", "c:/new_abc"))

# 在复制时,我们还可以指定忽略(排除)哪些文件(类型)。
# ignore接受通配符。
# *表示任意0到多个字符。
# ?表示任意1个字符
# []表示[]内部定义的字符。
# shutil.copytree("c:/abc", "c:/new_abc", ignore=shutil.ignore_patterns("*.ab"))

# 删除参数指定的目录。会将该目录内的子目录与文件一并删除(递归删除)。
# shutil.rmtree("c:/new_abc")
# 移动文件或目录。如果移动目录,则会将目录下的所有文件与子目录一同移动(递归移动)。
# 第1个参数:源文件或目录。
# 第2个参数:移动的位置。
# shutil.move("c:/abc", "c:/def")

# 在写入csv文件时,最好将newline参数设置为空""。(否则在excel会产生空行)
# with open("c:/def/test.csv", "wt", newline="") as f:
#     # writer函数返回一个写入器对象,该对象能够向参数指定的文件中写入数据。
#     writer = csv.writer(f)
#     # 一次写入一行记录
#     writer.writerow(["张某", 20, "A组"])
#     writer.writerow(["李某", 15, "B组"])
#     # 一次写入多行记录。提供一个二维列表。每个元素(一维列表)就是一条记录。
#     writer.writerows([["张某", 20, "C组"], ["张某", 20, "D组"]])

# 读取csv文件
# with open("c:/def/test.csv", "rt") as f:
#     # reader函数返回一个读取器对象,能够读取csv文件中的数据内容。
#     # 返回的对象是一个可迭代对象,可以放入for循环中。
#     reader = csv.reader(f)
#     for item in reader:
#         print(item)

# 上下文管理器用于with环境中。上下文管理器类需要定义__enter__与__exit__方法。
# 当进入with环境时,会调用上下文管理器类的__enter__方法(可以在该方法中执行一些
# 初始化的操作),当离开with环境时,就会调用上下文管理器的__exit__方法(可以在该)。
# 方法中执行一些清理的工作)。

#[字符] 匹配[]中的任意一个字符。[]还可以指定一个区间。
# 例如:a-z 匹配所有的小写字母。A-Z 匹配所有的大写字母。
# 0-9 匹配所有的阿拉伯数字。
# 如果需要匹配普通的-,],可以使用转义。除此之外,还可以把-置于[]的两端。
# ] 也可以置于[]的第一个字符(紧邻[之后)。

# [^字符]与[字符]是相反的操作。表示匹配任意一个不在[]当中的字符。

# \d 在str模式下,匹配任意的数字字符,包括但不限于(远远多于)[0-9]。
# 在bytes模式下,匹配[0-9]。

# \D \d的取反操作,匹配非数字。
# r = re.search(r"a\Db", "3a#bsdf")

# \s 在str模式下,匹配Unicode字符集任意的空白字符,包括但不限于[空格\t\v\r\n\f]。
# \S 对\s 的取反,匹配任意一个非空白字符。

# \w 在str模式下,匹配所有的Unicode字符集中的单词字符。包括但不限于[a-zA-Z0-9_](字母数字与下划线)。
# \W \w的取反操作,匹配任意一个非单词字符。

#findall 查找所有匹配的内容。返回一个列表。
li = re.findall("[0-9]", "a3a4x8i6")
print(li)

空间复杂度:空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。
时间复杂度:时间复杂度的计算并不是计算程序具体运行的时间,而是算法执行语句的次数

 

# 线程:线程是进程的基本执行单元。一个进程至少要存在一个线程。
# 进程具有独立的空间与系统资源,但线程不具有独立的系统资源,而同一个进程下的多线程共享该进程中的资源。

# 进程之间是独立的,一个进程想访问另外一个进程的数据,必须要得到另外一个进程的授权才可以(不能随便访问)。
# 同一个进程下的多个线程可以相互协作(可以相互访问)。

# Python提供了threading模块(Thread)类,来实现多线程的功能。
# 可以采用两种方式来创建线程:
# 1 使用Thread类,提供target参数。
# 2 继承Thread类,重写run方法。

# target参数指定线程要执行的任务。当线程运行时,就会执行(调用)该函数。
# args 指定函数的参数。(元组类型)
t = threading.Thread(target=mission, args=(3, 8))
# 仅仅创建了线程的对象,该线程还不能够得到执行。需要调用start方法来启动线程。
# 调用start之后,该线程就处于就绪状态。处于就绪状态的线程不代表会马上得到执行。
# 具体何时执行,要取决于操作系统的调度。
t.start()
t2 = threading.Thread(target=mission, args=(10, 19))
t2.start()

t = MyThread()
t2 = MyThread(start=15, end=23)
# 调用start与调用run,二者是不同的。尽管都能指定相同的程序代码。但是,调用start等于是开启了一个新的线程,
# 可以与其他线程并行执行。但是,调用run,没有开启新的线程,还是串行执行。
t.start()
t2.start()
# t.run()
# t2.run()


# 线程的声明周期:
# 1 新建。  创建了线程的对象。处于新建状态的线程不具有执行能力。
# 2 就绪。  调用start方法之后,线程就会处于就绪状态。一旦获得CPU的时间片,就可以得到执行。
# 3 运行。  就绪状态的线程获得了CPU的时间片,就可以得到执行。
# 4 阻塞。  应为某些条件没有满足,而处于等待过程中。(input)
# 5 死亡。  线程run方法执行完毕。(或run方法中抛出未捕获的异常)

 

同一时间是执行主进程还是子进程无法预知,完全取决于操作系统

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值