Python基础语法

集合(set)

集合:是一个不重复无序的元素序列,里面的元素不可重复。无序的。

set1 = {1, 3, 3}
set2 = set()
# 定义字典
set3 = {}

# 打印数据类型
type()
# 打印元素类型
print(f'set:{set1}')
# list 转换set
set4 = set([1, 2, 1,2])
# set 转换 list
list1 = list(set4)

集合公共操作符

函数名描述
len()计算容器中元素个数
del 或del()删除 自动补位
max()返回容器中元素最大值
min()返回容器中元素最小值
range(start,end,step)生成从start到end的数字,步长为step,供for循环使用,如果使用需要进行类型转换。list(range(1,3))
enumerate()函数用于将一个可遍历的数据对象(如列表,元素或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在for循环中 。格式:(集合,下标起始值)
l1 = range(1, 5)
en1 = enumerate(l1) # 输出为(0, 1) (1, 2) (2, 3) (3, 4) 
en2 = enumerate(l1, 2) # 输出为(2, 1) (3, 2) (4, 3) (5, 4) 指定起始角标

列表集合字典推导式

什么是推导式?推导式又称解析式,是python的一种独有特性,推导式是可以从一个数据序列构建另一个新的数据序列(一个有规律的列表或控制一个有规律列表)的结构体。共有三种推导:列表推导式,集合推导式,字典推导式

列表推导式

变量名 = [表达式 for 变量 in 列表 for 变量 in 列表 ]

变量名 = [表达式 for 变量 in 列表 if 条件]

l1 = [i for i in range(10)]
print(l1) [0,1,2,3,4,5,6,7,8,9]
l2 = [i for i in range(10) if i % 2 ==0]
print(l1) [0,2,4,6,8]
l3 = [(i,j) for i in range(1,5) for j in range(2)]
print(l1) [(1, 0), (2,1), (3, 0), (4,1)]
# 创建一个字段,字典key为1-5数字,value是这个数字的2次方
dic1 = {i: i**2 for i in range(5)}
print(dic1) #{1,1, 2:4, 3:9, 4:16}
# 将两人个列表合并为一个字典
list1 = ['name', 'age', 'gender']
list2 = ['Tom', 20, 'man']
dic2 = {list[i]:list2[i] for i in range(len(list1))}
print(dic2) {'name': 'Tom', 'age': 20, 'gender': 'man'}

集合推导式

变量名 = {表达式 for 变量 in 列表 for 变量 in 列表}

变量名 = {表达式 for 变量 in 列表 if 条件}

# 创建0-4的次方集合
set1 = {i ** 2 for i in ragne(5)}

函数

# 函数中的说明文档 
def get_sum(a, b):
    """
    计算两个数的和
    :param a:  参数1
    :param b:  参数2
    :return: 参数1和参数2的和
    """
    return a + b

变量作用域

局部变量:函数内使用的变量;存储到堆;生命周期与函数或类的生命周期一样,

全局变量:在函数内外都能使用,可以使用global关键字;存储到方法区;生命周期随着.py文件加载而存在,.py文件在内存中移除而消失。

使用变量遵循就近原则,

函数返回多个返回值

  • 当返回多个数据的时候,默认是元组类型
  • return后面可以连接列表,元组或字典,以返回多个值。
# 返回两个值,默认为元组类型
def reutn_num():
    return 1, 2 

函数参数

位置参数

位置参数:调用函数时根据函数定义的参数位置来传递参数。传递的参数和定义的参数的顺序及个数必须一致。是对于实参来讲的。

def user_info(name, age, gender):
    print(f'您的名字是{name}, 年龄是{age}, 性别是{gender}')
user_info('lisi', 20, '男');

关键字参数

关键字参数:函数调用时通过”键=值”形式传递参数,对于实参来讲的。

作用:可以让函数更加清晰,容易使用,同时也清除了参数的顺序需求。

注意:函数调用时,如果有位置参数时,位置参数必须在关键字参数的前面,但关键字参数之间不存在先后顺序

def user_info(name, age, gender):
	print(f'您的名字是{name}, 年龄是{age}, 性别是{gender}')    
# 调用时可以不按顺序来
user_info('lisi', gender='男', 23)

缺省参数

缺省参数:缺省参数也叫默认参数,用于定义函数,为参数提供默认值,调用函数时可不传该默认参数的值(注意:所有位置参数必须出现在默认参数前,包括函数定义和调用)

作用:调用函数时没有传递参数,就会使用缺省参数的对应值。

注意:函数调用时,如果为缺省参数传值则修改默认参数值,否则使用这个默认值

def user_info(name, age, gender='男'):
    print(f'您的名字是{name}, 年龄是{age}, 性别是{gender}')    
# 调
user_info('liSi', age=23)

不定长参数

不定长参数:不定长参数也叫可变参数,用于不确定调用的时候会传递多少个参数(不传参也可以)的场景,

作用:当调用函数时不确定参数个数时,可以使用不定长参数。

不定长参数的类型:位置传递,关键字传递

  • *args:传进的所有参数都会被args变更收集,它会根据传进参数的位置合并为一个元组(tuple),args是元组类型,这就是位置传递。应用于实体的个数不确定的情况,就可以把形参定义成可变参数。
  • **kwargs:参数是“键=值”形式的情况下,所有的键值都会被 kwargs接受,同时会根据键值组成字典。只能接收所有关键字参数,封装到字典中。
def user_info(*args):
    print(args)
    
def user_info(**kwargs):
    print(args)

拆包

拆包:对于函数中的多个返回数据,去掉无组,列表,或者字典,直接获取里面数据的过程。

一个元组中有多个元素,当我们想要获取元组中的每一个元素的时候就可以使用拆包获取。

字典拆包只能拿到key,不能拿到value,因为value是重复的,key是不重复的。

my_tup = (1, 3.14, 'hello') # 组包
num, pi, name = my_tup # 拆包

# 交换两个变量的值
a = 10
b = 20 
a,b = b, a

匿名函数

匿名函数:没有名字的函数叫做匿名函数

格式:变量名 = lambda 形参列表: return 返回值

应用场景:当对方法仅调用一次,匿名函数可以做为函数对象进行传递。

函数作为参数,是对功能逻辑的一种封装。

fn_sum = lambda a, b: a +b 
n = fn_sum(1, 2) # 3
# 函数作为参数
def custom_fun(a, b, fun):
    return fun(a, b)

res1 = custom_fun(1, 2, lambda a, b: a + b)
print(f'result: {res1}')
res2 = custom_fun(1, 2, lambda a, b: a * b)
print(f'result: {res2}')

文件操作

open(name, model):打开一个文件

name:要打开的文件名的字符串具体路径,

model:设置打开文件的模式 (只读,写入,追加)

  1. 国内主要使用GBK码表,1个中文占2个字节
  2. 国际通用码表UTF-8,1个中文占3个字节
  3. 无论是什么码表,英文字母,数字,特殊字符都占1个字节
  4. 只要出现乱码,说明变是编解码码表不一样
  5. 如果开头是负数,应该是中文编码,要么以GBK读两个字节来解析,要以3个字节UTF-8来解析。

格式:with open(‘路径’, ’模式’, ‘码表’) as 别名: 语句体

特点:语句体执行结束后,with后边定义的变量,会自动释放

f = open('python.txt', 'rw') # r只能读文本文件, 默认为系统码表
f.read() # 读所有内容
f.read(3) # 读3个字节 
f.readline() # 读一行
f.write("你好")
f.writelines("大家好")

with open('demo.txt', 'rb') as src_f, open('write.txt', 'wb') as dest_f:
    while True:
        data = src_f.read(8192)
        if len(data) <= 0:
            break
        dest_f.write(data)

异常

捕获常规异常

基本语法:
try:
    可能发生错误的代码 
except:
    如果出现异常执行的代码
# 捕获指定异常
try:
    可能发生错误的代码 
except (NameError, ZeroDivisionErr) as e:
    如果出现异常执行的代码
   
try:
    可能发生错误的代码 
except Exception as e:
    print(e)
异常的else:
try:
    可能发生错误的代码 
except Exception as e:
    print(e)
else:
    try中的代码无问题时执行,有问题才跳过。
异常的finally:
try:
    可能发生错误的代码 
except Exception as e:
    print(e)
else:
    try中的代码无问题时执行,有问题才跳过。
finally:
    无论是否异常都要执行的代码。

异常的传递

  1. 分享错误处理逻辑与核心逻辑
  2. 实现集中化错误处理
  3. 支持灵活的异常处理策略
  4. 保留完整的错误上下文信息

模块

什么是模块?

python模块(module)是一个python文件,以.py结尾,模块能定义函数,类和变量,模块里也能包含可执行的代码。

模块的作用?

每个模块都可以帮助我们快速实现一些功能,对功能的封装,模块就是一个工具包。

学习模块就是记忆一些.py文件及其中的一些函数。如random模块,time模块,os模块

导入模块的方式

  • import 模块名  后续通过模块名.函数名() 方式调用,模块下所有函数均可使用
  • import 模块名 as 别名 后续通过别名.函数名() 方式调用,模块下所有函数均可使用
  • from 模块名 import 函数名 后续通过函数名()的方式调用,只能使用该模块导入的函数
  • from 模块名 import 函数名 as 别名 后续通过别名()的方式调用,只能使用该模块导入的函数
  • from 模块名 import * 后续通过函数()的方式调用,模块下所有函数都可以使用

自定义模块

每个python文件都可以作为一个模块,模块的名字就是文件的名字,也就是说自定义模块名必须要符合标识符命名规则。

# __name__ 属性,可以解决模块自测的问题,当前模块中打印结果是__main__,在调用者模块中打印的是当前的模块名。
if __name__ == '__main__':
    如果为true,说明在当前模块中执行的

# __all

当导入多个模块时,且模块内有同名功能,当调用这个同名功能的时候,调用到的是最后面导入的模块的功能。

# __all__ 属性,只对针对于from 模块名 import *这种写法有效,它只会导入__all__记录的内容。
# 模块文件 只爆露指定的函数
__all__ = ['fun1', 'fun2']

什么是python的包?

从物理上看,就是一个文件夹,包含了一个__init__.py文件,该文件夹用于包含多个模块文件,从逻辑上看,包的本质依然是模块。

包的作用:管理模块,包含多个模块,但也是模块。

导包的方式:

1. improt 包.模块名

2 . from 包名 import * 但必须在__init__.py文件中添加__all__=[] 控制允许导入的模块列表。如果__init__.py中的all属性不写的话,无法导入;模块中的all不写是导所有。

3 . from 模块名 import 目标

os模块

全称叫: operation system, 系统模块,主要是操作文件夹,文件,路经,

常用函数:

  • getcmd():获取当前工作空间目录,current work directory
  • chdir():改变工作空间 change directory
  • rmdir():删除文件夹,必须是空文件夹
  • mkdir():创建文件夹,make directory
  • rename():改名,文件名或文件夹名

面向对象

面向对象的三大特征:封装,继承,多态
封装:把属性和方法封装在一起,仅提供对外的方法让别人访问,好处:简化编程
继承:子类复用父类的功能,好处:代码复用
多态:同样一个函数在不同场景下表现出不同形态,好处:解耦合,可扩展
格式:
class 类名(父类名):
​ pass
对象名 = 类名()
类外属性操作:
设置属性:对象名.属性 = 属性值
获取属性:对象名.属性
类内属性操作:
self.属性名=属性值

# 格式1
class Car:
    def run(self): # self代表当前本类对象的引用
        print("汽车会跑")
# 格式2
class Car():
    def run(self): # self代表当前本类对象的引用
        print("汽车会跑")
# 格式3
class Car(object): # 所有类的父类,所有有类直接或间接继承自objecst
    def run(self): # self代表当前本类对象的引用
        print("汽车会跑")
    def work(self):
        self.run()

 if __name__ == "__main__":
    c1 = Car()
    c1.run()

魔法方法 

概念:在python中,有一些可以给python类增加魔力的特殊方法,它们总是被双下划线所包围,我们称之为魔法方法,在特殊情况下会被自动调用,不需要开发者手动调用。格式为:__魔法方法名__()

__init__()方法

在python中,当新创建一个对象时,则会自动触发__init__()魔法方法,无参不需要外面传参数,有参则需要外面传参。可用于初始对象的属性值。

__str__()方法

用于快速打印对象的各个属性值的,在输出语句打印对象的时候会被自动调用 print(c1)

__del__()方法

当删除对象的时候调用,或者main函数执行完毕后,会自动调用,常用于释放资源。

class Car:
    def __init__(self, color, number):
        self.color = color
        self.number = number

    def __str__(self):
        return f"The Car Color is {self.color}, that number is {self.number}"
    def __del__(self):
        print("The car object is del.")

继承

面向对象中的继承:指子类继承父类的属性和方法,是的类的继承,在Python中所有类默认继承object类,object类是顶级类或基类,其他子类叫做派生类。
继承可以提升代码的复用性。
单继承:一个子类只能继承一个父类,不能继承多个类。

class Father(object):
    def __init__(self):
        self.gender = "男"

class Son(Father):
    def sayGender(self):
        print(f"My Son gender is {self.gender}")

多继承:一个类同时继承了多个父类,并且同时具有所有父类的属性和方法。如果有相同属性和方法时,优先继承第一个父类的同名属性和方法。

当一个类有多个父类时,默认使用第一个父类的同名属性和方法,可以使用类名.__mro__属性或类名.mro()方法查看调用的先后顺序。

class Father(object):
    def __init__(self):
        self.gender = "男"

class Mather(object):
    def __init__(self):
        self.gender = "女"
    def make_cook(self):
        print("做饭")

class Son(Father, Mather):
    def sayGender(self):
        print(f"My Son gender is {self.gender}")

重写

重写也叫作覆盖,就是当子类属性或方法与父类的属性或方法名字相同时,从父类继承下来的成员可以重新定义,优先调用子类的属性和方法

子类调用父类的方法:父类名.父类方法(self),super().父类方法名(),多继承不建议使用super

class Son(object):
    def do_soming(self):
        Father.__init__(self)
        Father.do_soming(self)

封装

私有属性与方法
在python中,可以为属性和方法设置为私有权限,即设置某个属性或方法不继承给子类,设置私有属性与方法的方法:在属性或方法名前加上__,格式:__属性名,def __方法名(),私有属性与方法,只能在类的内部使用,不能在类的外部使用。

class PrivateClass(object):

    def __init__(self, title):
        self.__title = title

    def say_upper_title(self):
        print(f"Say Upper Title {self.__trans_to_upper()}")

    def __trans_to_upper(self):
        return self.__title.upper()

多态

多态,指的是:多种状态,比如同一个函数在不同场景下有不同的状态。python中的多态是伪多态。通过多态语法轻松的实现模块和模块之间的解耦合,实现了软件系统的可拓展。

实现多态有三个条件:

  1. 有继承(定义父类,宝义子类,子类继承父类)
  2. 函数重写(子类重写父类的函数)
  3. 父类引用指向子类对象(子类对象传递给父类对象调用者)
class Animal(object):
    def speak(self):
        pass
class Dog(Animal):
    def speak(self):
        print("Dog speak")
class Cat(Animal):
    def speak(self):
        print("Cat speak")

def animal_speak(animal:Animal):
    animal.speak()

if __name__ == '__main__':
    dog = Dog()
    cat = Cat()
    animal_speak(dog)
    animal_speak(cat)

抽象类(接口)

方法中没有具体的实现,只有一个pass关键的方法称为抽象方法,有抽象方法的类叫做抽象类或接口,设计的含义:父类用来确定有那些方法(父类制定接口标准),具体的方法实现有子类来实现(子类实现接口标准)

class Person(object):
    def say_gender():
        pass
class Man(Person):
    def say_gender():
        print("Gender is Man")
class Woman(Person):
    def say_gender():
        print("Gender is Woman")

其它属性

类属性:指的就是类所拥有的属性,它被共享于整个类中(都可以直接调用),调用格式:类名.类属性名,对象名.类属性名
对象属性:属于每个对象的属性,调用格式:self.属性名,对象名.属性名
如果一个变更是被该类下所有的对象所共享的,就要考虑定义为类变量。

class Person(object):
    count = 1
 if __name__ == '__main__':
    Person.count = 2

类方法:就是类所有拥有的方法,并需要使用装饰火龙@classmethod来标识其为类方法,对于类方法的第一个参数必须是类对象,通过以cls作为第一个参数名。调用格式为:类名.类方法

class Book(object):
    @classmethod
    def say_title(cls):
        print("Book Title")

if __name__ == '__main__':
    Book.say_title()

静态方法:需要通过装饰器@staticmethod来标识其为静态方法,且静态方法不需要多定义参数。调用方法为:类名.静态方法名()

class Book(object):
    @staticmethod
    def say_author():
        print("Book author")

if __name__ == '__main__':
    Book.say_author()

类方法与静态方法的区别:是否需要使用该类的对象,如果使用则定义类方法,不需要定义成静态方法,如果非要定义成静态方法,则可以使用类名.类属性调用类属性与类方法

闭包

闭包的作用:闭包可以保存函数内的变量,而不会随着调用函数而被销毁。

闭包的定义:在函数嵌套的前提下,内部函数使用外部函数的变量,并且外部函数返回了内部函数,这种使用外部函数变量的内部函数称为闭包。

# 外部函数
def 外部函数名(外部参数):
    # 内部函数 
    def 内部函数名(内部参数):
        # 使用外部函数的变量
   	return 内部函数名 # 闭包

闭包构成的条件:

  1. 有嵌套:在函数嵌套(函数里再定义函数)的前提下
  2. 有引用:内部函数使用了外部函数的变量(包括外部函数的参数)
  3. 有返回:外部函数返回了内部函数名。
def op_fn(n1: int, op: str):
    def add(n2: int):
        return n1 + n2
    def sub(n2: int):
        return n1 - n2
    if "+"  == op:
        return add
    elif "-" == op:
        return sub
    return None

if __name__ == '__main__':
    res = op_fn(10, "+")(20)
    print(f"close fn result: {res}")

闭包关键字:
global:全局变量
nonlocal:声明能够让内部函数去修改外部函数的变量

def fn_outer(op:str):
    num = 10
    def add_fn(n1):
        nonlocal num
        num  += n1
        return num
    def sub_fn(n1):
        nonlocal num
        num -= n1
        return num
    if "+" == op:
        return add_fn
    elif "-" == op:
        return sub_fn
    else:
        return None


if __name__ == '__main__':
    res = fn_outer("+")(10)
    print(f"fn outer res: {res}") # 20
    res = fn_outer("-")(20)
    print(f"fn outer res: {res}") # -10
    op_fn = fn_outer("+")
    res = op_fn(10)
    print(f"fn outer res: {res}") # 20
    res = op_fn(10)
    print(f"fn outer res: {res}") # 30

装饰器

语法糖:就是语法简化的写法
装饰器:在不改函数的基础上,给原有函数增加额外的功能 ,装饰器本质上就是一个闭包函数。内部函数的形式,必须和原函数(要被装饰的函数)形式一致,即要有参数都有参,要有返回值就都有返回值。
装饰器构成的条件:

  1. 有嵌套:在函数嵌套(函数里再定义函数)的前提下
  2. 有引用:内部函数使用了外部函数的变量(包括外部函数的参数)
  3. 有返回:外部函数返回了内部函数名。
  4. 有客外的功能:给需求装饰的原有函数增加额外功能
    装饰器语法:
# 传统方法
变量名 = 装饰器名(原有函数)
# 语法糖
变量名 = @装饰器 (加上函数上面)
# 调用
变量名()

# 无参无返回 
def check_login(fn_name):
    def inner():
        print("登录中...")
        print("登录成功...")
        fn_name()
    return inner

@check_login
def comment():
    print("发表评论")

if __name__ == '__main__':
    # 传统方式调用
    # check_login(comment)
    # 语法糖方式调用
    comment()
    
# 有参有返回
def fn_before(fn):
    def inner(n1, n2):
        if isinstance(n1, int) and isinstance(n2, int):
            return fn(n1, n2)
        else:
            return None
    return inner

@fn_before
def fn(n1, n2):
    return n1 + n2

if __name__ == '__main__':
    res = fn(1, 2)
    print(f"res: {res}") # res: 3
    res = fn(1, 'a')
    print(f"res: {res}") # res: None
# 多个装饰器装饰一个函数,按顺序执行装饰器,最后执行离函数最近的装饰器

有参数的装饰器

def op_decorate(flag):
    def decorate(fn_name):
        def inner_add(n1, n2):
            fn_name(n1, n2)
            return n1 + n2
        def inner_sub(n1, n2):
            fn_name(n1, n2)
            return n1 - n2
        if "+" == flag:
            return inner_add
        elif "-" == flag:
            return inner_sub
        else:
            return fn_name
    return decorate

@op_decorate("+")
def op_num(n1, n2):
    return 0

if __name__ == '__main__':
    res = op_num(10, 20)
    print(f"res: {res}") # res: 30

多进程

子进程对象 = multiprocessing.Process(group=None, targe=None, name=None, args=(), kwargs = {})

  • group:参数未使用,值始终为None
  • target+表示调用对象,子进程要执行的任务
  • args:表示以元组的形式向子任务函数传参,元组传参一定要和参数列表顺序保持一致
  • kwargs:表示以字典的方式给子任务函数传参,字典方式传参字典中的key要和参数名保持一致.
  • name:子进程的名称

多线程

import threading
import time


def coding(name):
    var = threading.current_thread().name
    while True:
        print(f"{var} --> {name} coding....\n")
        time.sleep(1)

def music(name):
    var = threading.current_thread().name
    while True:
        print(f"{var} --> {name} music.....\n")
        time.sleep(1)

if __name__ == '__main__':
    t1 = threading.Thread(target=coding, name='Coding', args=("xx",))
    t2 = threading.Thread(target=music, name='Music', kwargs={"name": "LiSi"})
    t1.start()
    t2.start()

num = 0
mutex = threading.Lock()

def set_num_one():
    mutex.acquire()
    global num
    for i in range(100):
        num += 1
    mutex.release()
    print(f'set_num_one函数,累加后的结果为:{num}')

def set_num_tow():
    mutex.acquire()
    global num
    for i in range(100):
        num += 1
    mutex.release()
    print(f'set_num_two函数,累加后的结果为:{num}')

if __name__ == '__main__':
    t1 = threading.Thread(target=set_num_one, name='set_num_one')
    t2 = threading.Thread(target=set_num_tow, name='set_num_two')
    t1.start()
    t2.start()
    print("main is over...")

上下文管理器

一个类只要实现了__enter__()和__exit__()这两个方法,通过该类创建的对象我们称之为上下文管理器。with 语法功能底层用到的就是上下文管理器。其中__enter__()会在with语句之前执行,常用于初始化对象,__exit__() 会在with语句后执行,一般用于释放资源。

class My_File(object):
    def __init__(self, file_name, file_model):
        self.file_name = file_name
        self.file_model = file_model
        self.file_obj = None

    def __enter__(self):
        self.file_obj = open(self.file_name, self.file_model, encoding="utf-8")
        return self.file_obj
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file_obj:
            self.file_obj.close()
        print("文件释放")


if __name__ == '__main__':
    with My_File("./main.py", "r") as f:
        line = f.readline()
        print(f"文件内容:{line}")

生成器

什么是生成器?
根据程序员制定的规则循环生成数据,当条件不成立时则生成数据结束。数据不是一次生全部生成出来,而是使用一个,再生成一个,可以节约大量的内存。如果生成器中没有数据的话,会报错。
创建生成器的方式: 生成器推导式,yield关键字(可以临时存有所有数据,并放到生成器中,调用函数时会返回 一个生成器对象)
yield关键字的作用:创建生成器,把值放到生成器中,返回生成器。
使用生成器的方式:next函数,遍历生成器即可

# 生成器推导式
gen = (i for i in range(5))
print(next(gen))
print("-" * 21)
for i in gen:
    print(i)
# yield生成器
def gen():
    for i in range(5):
        yield i
fn_gen = gen()
print(next(fn_gen)) # 0
print(next(fn_gen)) # 1

@Property

它是用来修饰函数的,目的是:简化代码开发
格式:

  • property充当装饰器的用法
    • @property 修饰是的获取值的方法
    • @property.setter 修饰的是设置值的方法 ,@propert修饰的方法 名
  • property充当类变量
    • 类变量名 =property(获取值的方法,设置值的方法 )
# 充当装饰器
class Student(object):
    def __init__(self):
        self.__name = ''

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, new_name):
        self.__name = new_name


if __name__ == '__main__':
    s = Student()
    s.name = "四经"
    print(f"Student name: {s.name}")

# 充当类变量
class Teacher(object):
    def __init__(self):
        self.__name = ''

    def get_name(self):
        return self.__name
    def set_name(self, name):
        self.__name = name

    name = property(get_name, set_name)

if __name__ == '__main__':
    s = Teacher()
    s.name = "教师"
    print(f"Teacher name: {s.name}")