一、面向对象第一天
1、类的组成:类(Class) 由3个部分构成
- 类的名称:类名
- 类的属性:一组数据
- 类的方法:允许对进行操作的方法 (行为)
2、类的定义和实例化语法及演示
"""
类的定义:
python3中,类名后面加不加括号都可以,不佳括号或者是括号中没有父类名称,默认都是继承Object类
"""
class 类名(父类类名):
# Python的类提供了一个方法,方法名具有特定的格式,格式是:__方法名__(),即由开始和结尾都是由两个下划线组成这种方法叫做魔法方法。
# 魔法函数中的构造方法的名称一定是 __init__
# 如果自定义类中没有写构造函数,python会自动提供一个构造函数,但是不执行任何操作,
# 所以一定要提供构造函数
def __init__(self,args...): # args : 代表参数列表,用于在实例化对象的时候,给对象中的属性赋值
"""__init__():该方法就是当前类的构造函数,用来做变量的 初始化 或者 赋值 操作,在实例化类的时候就会自动执行该方法"""
self.arg1 = arg1 # self.arg1 : 给类定义属性
# = arg1 : 在实例化的时候给变量(属性)赋值
self.arg2 = arg2
# 方法
def 方法名(self,args): # 该方法叫做实例方法,第一个参数必须是self,self代表的是类本身
方法体...
#### 在类中获取属性(变量)或者方法,直接使用self.属性名或者self.方法名()即可
"""
类的实例化:
"""
类对象名 = 类名(arg1,arg2,arg3) # 实例化对象的时候,参数列表默认传递到构造函数中,进行变量的赋值和初始化
# 方法调用
类对象名.方法名() # 当类的对象调用实例方法的时候,Python会自动将类本身的引用作为参数,传递到实例方法的第一个参数中
#### 在类外面获取属性(变量)或者方法,需要使用实例化的类对象名进行调用,如:类对象名.属性名或者类对象名.方法名()
定义类和实例化类的代码演示:
# 定义一个类
class Persion():
# 构造方法
def __init__(self, name, age, gender, address):
# 在此处定义属性和初始化属性的值
self.name = name
self.age = age
self.gender = gender
self.address = address
print(self) # 此处打印的是实例化类对象的时候的地址值
# 其他成员方法
def run(self):
print("%s的年龄是%d,性别是%s,出生地是%s,与生俱来的技能是会跑" % (self.name, self.age, self.gender, self.address))
def hunt(self):
print("%s学会了打猎" % self.name)
# 实例化对象
p1 = Persion("尧", 5000, "男", "黄河流域")
p1.run()
p1.name = "舜"
p1.hunt()
"""
打印结果:
<__main__.Persion object at 0x00000174E0C997B8>
尧的年龄是5000,性别是男,出生地是黄河流域,与生俱来的技能是会跑
舜学会了打猎
"""
3、魔法函数中的__str__函数
# 定义一个类
class Persion():
# 构造方法
def __init__(self, name, age, gender, address):
# 在此处定义属性和初始化属性的值
self.name = name
self.age = age
self.gender = gender
self.address = address
print(self) # 如果写了__str__方法,此处就会打印__str__方法的返回值;
# 如果没有写__str__方法,此处打印的是实例化类对象的时候的地址值
# __str__魔法函数的使用
def __str__(self): # 该方法需要返回值,返回的是字符串;该方法作用类似于java中的toString()方法
return "在实例化对象之后,直接打印对象名称的时候,默认会调用__str__方法,类似于java中的toString()方法"
# 实例化对象
p1 = Persion("启", 4500, "男", "夏王朝")
print(p1)
"""
打印结果:
在实例化对象之后,直接打印对象名称的时候,默认会调用__str__方法,类似于java中的toString()方法
在实例化对象之后,直接打印对象名称的时候,默认会调用__str__方法,类似于java中的toString()方法
"""
4、案例:模拟LOL中英雄solo场景
import random
# 定义一个英雄类
class Hero:
# 构造方法
def __init__(self, name, gong, fang, jineng, ph):
self.name = name
self.gong = gong
self.fang = fang
self.jineng = jineng
self.ph = ph
# 魔法函数中的str方法,用于打印英雄相关信息
def __str__(self):
return "%s的生命值为%d,攻击力为:%d,防御力为:%d,绝招是%s "%(self.name, self.ph, self.gong, self.fang, self.jineng)
# 定义攻击的方法,参数是敌人对象
def attack(self, enemy):
# 定义随机数,随机进行暴击,概率为50%
baoji = random.randint(1, 2)
# 计算出每次攻击造成的伤害
shanghai = self.gong * baoji - enemy.fang
# 计算出攻击之后敌人的血量剩余值
enemy.ph = enemy.ph - shanghai
if baoji == 1:
print("普通攻击,%s对%s进行了普通攻击,造成了%d的伤害" % (self.name, enemy.name, shanghai))
elif baoji == 2:
print("【暴击伤害】,%s对%s进行了绝招攻击,造成了%d的伤害" % (self.name, enemy.name, shanghai))
"""
实例化对象,进行solo
"""
aixi = Hero("艾希", 150, 80, "穿云箭", 2000)
ez = Hero("伊泽瑞尔", 130, 85, "集中弹幕", 2200)
round_num = 1
while True:
print("当前进行第%d回合!!!" % round_num)
print(aixi)
print(ez)
print()
# aixi对ez进行攻击
aixi.attack(ez)
if ez.ph <= 0:
print("GAME OVER!!!,%s拿到了一血,将%s打倒在地!!!!" % (aixi.name, ez.name))
break
# ez对aixi进行攻击
ez.attack(aixi)
if aixi.ph <= 0:
print("GAME OVER!!!,%s拿到了一血,将%s打倒在地!!!!" % (ez.name, aixi.name))
break
# 回合结束,次数加一,继续攻击
round_num += 1
print("-----" * 10)
二、面向对象第二天
1、继承的语法
# 定义一个父类
class Master:
pass
# 定义一个子类
class tudi(Master): # 定义类的时候,在()中加上需要继承的类的类名,这个方式就叫做继承
# 继承的子类中具有父类对象的所有非私有的属性和方法
pass
2、多继承
class Master(object):
name = "老师傅"
# 成员方法
def cookie(self):
print("<%s>教的做菜方法" % self.name)
def speak(self):
print("老师傅教会了说话")
class School(object):
name = "学校"
# 成员方法
def cookie(self):
print("<%s>的做菜方法" % self.name)
def beMan(self):
print("学校教会了做人的方法")
# 多继承继承了多个父类,同时也继承了所有父类的属性和方法
# ⭐⭐⭐类Student1后面的父类中首先继承Master,如果两个父类中具有同名的属性和方法,用的也是首先继承的Master的属性和方法
# 两个父类中各自独有的方法,都会被子类继承
class Student1(Master, School):
pass
# 实例化Student对象
stu1 = Student1()
stu1.cookie() # <老师傅>教的做菜方法
stu1.speak() # 老师傅教会了说话
stu1.beMan() # 学校教会了做人的方法
# ---------------------------------------------------------------------
class Student2(School, Master):
pass
stu2 = Student2()
stu2.cookie() # <学校>的做菜方法
stu2.speak() # 老师傅教会了说话
stu2.beMan() # 学校教会了做人的方法
3、子类重写父类中的方法和属性
class Master(object):
name = "老师傅"
# 成员方法
def cookie(self):
print("<%s>教的做菜方法" % self.name)
def speak(self):
print("老师傅教会了说话")
class School(object):
name = "学校"
# 成员方法
def cookie(self):
print("<%s>的做菜方法" % self.name)
def beMan(self):
print("学校教会了做人的方法")
# ⭐⭐⭐如果子类中的方法和属性与父类中的重名,则默认使用子类的方法和属性
class Student1(Master, School):
name = "子类自己"
def cookie(self):
print("<%s>的方法" % self.name)
# 实例化Student对象
stu1 = Student1()
print(stu1.name) # 子类自己
stu1.cookie() # <子类自己>的方法
stu1.speak() # 老师傅教会了说话
stu1.beMan() # 学校教会了做人的方法
4、子类调用父类的同名方法
class Master(object):
def __init__(self):
self.name = "老师傅"
print("Master实例化")
# 成员方法
def cookie(self):
print("<%s>教的做菜方法" % self.name)
def speak(self):
print("老师傅教会了说话")
class School(object):
def __init__(self):
self.name = "学校"
print("School实例化")
# 成员方法
def cookie(self):
print("<%s>的做菜方法" % self.name)
def beMan(self):
print("学校教会了做人的方法")
# 如果子类中的方法和属性与父类中的重名,则默认使用子类的方法和属性
# ⭐⭐⭐但是如果想保留子类与父类同名方法的同时,还想调用父类的同名方法,需要重新写一个方法,去调用父类的同名方法
class Student1(Master, School):
def __init__(self):
self.name = "子类自己"
def cookie(self):
# 如果当前方法与父类方法重名,同时在当前类中也调用了父类的重名方法,在调用自己的重名方法时,首先要调用init方法
self.__init__()
print("<%s>的方法" % self.name)
# 重新写一个方法,在该方法体内调用父类的同名方法,需要传递参数self
def useMasterCookie(self):
Master.__init__(self) # 调用父类的方法时,并没有创建父类对象,如果不重新调用父类的构造方法,下一行代码调用的还是子类的属性
Master.cookie(self) # 同过父类类名.父类方法名的方式,可以调用父类的任意非私有的方法
def useSchoolCookie(self):
School.__init__(self)
School.cookie(self)
# 实例化Student对象
stu1 = Student1()
print(stu1.name) # 子类自己
stu1.cookie() # <子类自己>的方法
stu1.speak() # 老师傅教会了说话
stu1.beMan() # 学校教会了做人的方法
# 如果useMasterCookie方法不首先调用父类的init方法,则一下打印结果是 : <子类自己>教的做菜方法
stu1.useMasterCookie() # 首先调用父类的init方法,结果是: Master实例化 <老师傅>教的做菜方法
# 如果useSchoolCookie方法不首先调用父类的init方法,则一下打印结果是 : <子类自己>的做菜方法
stu1.useSchoolCookie() # 首先调用父类的init方法,结果是: School实例化 <学校>的做菜方法
# 如果在Student1类中的cookie方法中没有调用自己的init方法,则打印结果是 : <学校>的方法
stu1.cookie() # 调用了自己的init方法之后打印结果是: <子类自己>的方法
5、私有方法、私有属性
class Master(object):
def __init__(self):
self.name = "老师傅"
print("Master实例化")
# 成员方法
def cookie(self):
print("<%s>教的做菜方法" % self.name)
def speak(self):
print("老师傅教会了说话")
class School(object):
def __init__(self):
self.name = "学校"
print("School实例化")
# 成员方法
def cookie(self):
print("<%s>的做菜方法" % self.name)
def beMan(self):
print("学校教会了做人的方法")
# 如果子类中的方法和属性与父类中的重名,则默认使用子类的方法和属性
# 但是如果想保留子类与父类同名方法的同时,还想调用父类的同名方法,需要重新写一个方法,去调用父类的同名方法
"""
⭐⭐⭐ 私有属性和方法:
1、类的私有属性,不能通过对象直接访问,但是可以在本类中访问
2、类的私有方法,不能通过对象直接调用,但是可以在本类中调用
3、类的私有属性和私有方法,不能被子类继承,同时子类也不能访问父类的私有属性和方法
4、私有属性和私有方法都是不对外公开的
⭐⭐⭐ 给属性和方法添加私有权限的方法:
在方法或者属性前面加上两个下划线_即可
例如:
私有的属性:self.__name = "张三"
私有的方法:def __func():
"""
class Student1(Master, School):
def __init__(self):
self.name = "子类自己"
# ⭐⭐⭐ python中给属性设置私有,只需要在属性名前面加上两个_即可
self.__money = 10000
# ⭐⭐⭐ __money属性是私有的,如果想通过对象去调用属性,需要定义一个方法,返回对象的私有属性,不能直接访问私有属性
def getMoney(self):
return self.__money
# ⭐⭐⭐ __money属性是私有的,如果想通过对象去给money赋值,也需要定义一个方法,来操作私有属性
def setMoney(self, money):
self.__money = money
def cookie(self):
# 如果当前方法与父类方法重名,同时在当前类中也调用了父类的重名方法,在调用自己的重名方法时,首先要调用init方法
self.__init__()
print("<%s>的方法" % self.name)
# 重新写一个方法,在该方法体内调用父类的同名方法,需要传递参数self
def useMasterCookie(self):
Master.__init__(self) # 调用父类的方法时,并没有创建父类对象,如果不重新调用父类的构造方法,下一行代码调用的还是子类的属性
Master.cookie(self) # 同过父类类名.父类方法名的方式,可以调用父类的任意非私有的方法
def useSchoolCookie(self):
School.__init__(self)
School.cookie(self)
# 定义多层集成类,继承Student1,此时doubExtends类中具有父类和父类的父类的所有公共属性和方法
class doubExtends(Student1):
pass
# 实例化Student对象
stu1 = Student1()
print(stu1.name) # 子类自己
# print(stu1.__money) # 报错,私有属性不能直接访问 AttributeError: 'Student1' object has no attribute '__money'
print(stu1.getMoney()) # 10000
stu1.setMoney(12000)
print(stu1.getMoney()) # 12000
6、__del__析构函数
作用:当对象被删除或者调用结束的时候,会调用该函数
class Persion:
# 构造函数
def __init__(self, name, age):
self.name = name
self.age = age
# get/set方法
def get_age(self):
return self.age
def set_age(self, age):
if age < 0 | age > 100:
print("输入的年龄不合法")
else:
self.age = age
"""
析构函数:
当对象被删除或者程序结束时调用该方法
"""
def __del__(self):
print("调用了析构函数,对象被删除")
p1 = Persion("Python", 28) # 调用了析构函数,对象被删除
7、super() 执行父类方法
class Master(object):
def __init__(self):
self.name = "老师傅"
print("Master实例化")
# 成员方法
def cookie(self):
print("<%s>教的做菜方法" % self.name)
class School(object):
def __init__(self):
self.name = "学校"
print("School实例化")
# 成员方法
def cookie(self):
print("<%s>的做菜方法" % self.name)
class Student(Master, School):
def __init__(self):
self.name = "子类自己"
def cookie(self):
print("<%s>的方法" % self.name)
super(Student, self).__init__()
super(Student, self).cookie()
class Persion(Student):
def __init__(self):
self.name = "徒孙自己"
def cookie(self):
print("%s自己的方法" % self.name)
# 定义一个方法,可以执行所有的cookie方法(父类的和自己的)
def cookie_all(self):
# Master.__init__(self)
# Master.cookie(self)
# School.__init__(self)
# School.cookie(self)
# Student.__init__(self)
# Student.cookie(self)
# self.__init__()
# self.cookie()
"""
以上这种方式虽然可以同时执行所有的父类中的同名方法,但是存在以下问题:
1、子类继承了多个父类,如果父类的名称修改了,子类也需要涉及多次修改
2、子类继承了多个父类,需要重复写很多次,显得代码臃肿
此时,可以使用super()函数来解决调用父类的方法的问题
"""
"""
使用super() 可以逐一调用所有父类的方法,并且只执行一次,调用顺序遵循:__mro__属性(类的属性)
子类名.__mro__ 该方法用来获取所有父类调用的顺序
如果子类继承了多个父类,则__mro__属性只执行第一个继承的父类
代码如下:
super(子类名称, self).__init__()
super(子类名称, self).子类中的方法名()
注意:super()方法比较适合单继承的多层继承机制,super()目前不支持执行多个父类,只能执行第一个
"""
# 写法第一种
# self.cookie()
# super(Persion, self).__init__()
# super(Persion, self).cookie()
# 写法第二种
self.cookie()
super().__init__()
super().cookie()
print(Persion.__mro__)
# (<class '__main__.Persion'>, <class '__main__.Student'>, <class '__main__.Master'>, <class '__main__.School'>, <class 'object'>)
p1 = Persion()
p1.cookie_all()
"""
运行结果:
<子类自己>的方法
Master实例化
<老师傅>教的做菜方法
"""
8、多态
python中只要具有某种属性,即可认为是同一种类,这就是python的多态
class Hero:
# 构造函数
def __init__(self, name, gong, fang):
self.name = name
self.gong = gong
self.fang = fang
self.weapon = None
def __str__(self):
return "英雄<%s>数据:攻击力:%d 防御力:%d" % (self.name, self.gong, self.fang)
def get_weapon(self, weapon):
if not self.weapon:
pass
else:
print("英雄<%s>卸下了武器<%s>" % (self.name, self.weapon.name))
self.gong -= self.weapon.gong
self.fang -= self.weapon.fang
print(self)
self.weapon = weapon
print("英雄<%s>装备了武器<%s>" % (self.name, self.weapon.name))
self.gong += weapon.gong
self.fang += weapon.fang
print(self)
print()
# 定义武器类
class Weapon:
def __init__(self, name, gong, fang):
self.name = name
self.gong = gong
self.fang = fang
# 实例化英雄对象
timo = Hero("提莫", 120, 50)
# 实例化武器对象
ldl = Weapon("兰德里面具", 120, 20)
sgz = Weapon("时光杖", 70, 50)
timo.get_weapon(ldl)
timo.get_weapon(sgz)
"""
打印结果:
英雄<提莫>装备了武器<兰德里面具>
英雄<提莫>数据:攻击力:240 防御力:70
英雄<提莫>卸下了武器<兰德里面具>
英雄<提莫>数据:攻击力:120 防御力:50
英雄<提莫>装备了武器<时光杖>
英雄<提莫>数据:攻击力:190 防御力:100
"""
9、类属性、实例属性、类方法、静态函数
"""
类属性、实例属性、类方法、静态函数
"""
class Persion(object):
"""
类属性:在方法外部、类内部定义的属性
1、类属性可以被本类中的所有对象共享,而且在内存中值创建一次
2、类属性也支持访问权限的控制,即可以进行私有修饰
3、类属性定义名称时一般加上前缀cls(习惯而已,非强制性)
"""
cls_name = "张三"
__cls_age = 18
"""
构造函数
"""
def __init__(self, gender, address):
self.gender = gender # 实例属性
self.address = address # 实例属性
"""
类方法:
1、@classmethod 叫做装饰器,作用是用来修饰一个函数,让这个函数变成类方法,从而具有特殊功能
2、类方法一般都是用来处理类属性的
3、类方法可以被对象和类访问
"""
@classmethod
def get_cls_age(cls):
# 括号中的 cls 代表当前类,是类方法的唯一参数,同 self 类似,只不过 self 代表的是类的实例化对象
# self 用来访问实例属性,cls 用来访问类属性
# self 和 cls 都不需要实参,由 python 提供参数
return cls.__cls_age # 此种写法是通过类访问类属性并返回
@classmethod
def set_cls_age(cls, age):
cls.__cls_age = age
"""
静态函数:
1、静态函数使用的装饰器是:@staticmethod
2、静态函数不需要接收 self 对象或者 cls 对象
3、静态函数就是类里面的普通函数
4、如果一个函数不需要访问实例属性和实例方法或者是类属性和类方法,就可以定义为静态函数
"""
@staticmethod
def calc(a, b):
return a + b
"""
实例方法:
"""
def print_info(self):
# 通过对象访问类,然后通过类去调用类属性
print("类属性__cls_age的值是:%s" % self.__class__.__cls_age)
# 通过对象直接调用类属性
print("类属性__cls_age的值是:%s" % self.__cls_age)
p1 = Persion("男", "北京顺义")
p1.print_info() # 类属性__cls_age的值是:18
print(p1.calc(10, 20)) # 30
p1.set_cls_age(22)
print(p1.get_cls_age()) # 22
三、面向对象第三天
1、__new__函数
class Persion(object):
"""
__new__方法在构造方法之前执行用来创建实例化对象,并且必须返回这个实例化对象
如果__new__方法返回的是当前类的实例化对象,则接下来会调用__init__方法,
否则不调用__init__方法,并且实例化类对象的时候返回的是None
如果没有写__new__方法,默认执行的就是父类的__new__方法去创建对象,如果重写了,就必须调用父类的__new__方法
"""
def __new__(cls, *args, **kwargs):
print("__new__方法被调用")
obj = super().__new__(cls)
return obj
def __init__(self):
print("构造方法被调用了")
p1 = Persion()
print(p1) # <__main__.Persion object at 0x00000235BCB59748>
p2 = Persion()
print(p2) # <__main__.Persion object at 0x00000235BCB597B8>
print(p1 == p2) # False
2、单例模式
"""
单例模式:不管做了多少次实例化操作,都只创建了一个类的对象
"""
class Persion(object):
# 定义私有的类属性,用来保存实例对象
__cls_instance = None
# 定义类的私有变量,用来判断是否是第一次实例化对象(不属于单例模式的内容)
__isFirst = True
def __new__(cls, *args, **kwargs):
if cls.__cls_instance == None:
print("__new__方法被调用,第一次创建对象")
cls.__cls_instance = super().__new__(cls)
return cls.__cls_instance
"""
以下这种写法不属于单例模式
"""
def __init__(self, name, age, address):
if self.__isFirst:
self.name = name
self.age = age
self.address = address
self.__isFirst = False
print("构造方法被调用了")
def __str__(self):
return "Persion [name= %s, age= %d, address= %s]" % (self.name, self.age, self.address)
p1 = Persion("司马老贼", 72, "河内温县")
print(p1)
p2 = Persion("诸葛老儿", 56, "南阳卧龙岗")
print(p2)
print(p1 == p2)
"""
打印结果如下:
__new__方法被调用,第一次创建对象
构造方法被调用了
Persion [name= 司马老贼, age= 72, address= 河内温县]
Persion [name= 司马老贼, age= 72, address= 河内温县]
True
"""
3、捕获异常
异常处理语法:
"""
异常用来处理可能会出现的代码问题
将代码放到try中,如果代码出现异常,则立刻跳转到最近的exception中去处理
try中尽量只写一行代码,就是可能出现问题的代码,再去针对异常进行处理
"""
try:
正常执行的代码
except 错误类型1 as e:
报错时要执行的操作
print(e)
except 错误类型2 as e:
报错时要执行的操作
print(e)
except Exception as e:
报错时执行的代码
print(e)
else:
代码没有异常,就执行此处的操作
finally:
不管程序有没有异常,此处的代码都会执行
4、自定义异常以及抛出异常
# raise : 用来抛出异常
# 自定义异常类,需要继承父类Exception
class AgeExceptionErr(Exception):
# 构造函数
def __init__(self):
self.error_msg = "A个Error:年龄不符合1~100之间的要求"
def __str__(self):
return self.error_msg
class Persion:
# 构造函数
def __init__(self):
self.__age = 0
def set_age(self, age):
if not 0 <= age <= 100:
# 抛出异常,此时抛出的是自定义异常类
raise AgeExceptionErr
else:
self.__age = age
def get_age(self):
return self.__age
try:
p1 = Persion()
p1.set_age(300)
except AgeExceptionErr as e:
print(e.error_msg) # A个Error:年龄不符合1~100之间的要求
print(e) # A个Error:年龄不符合1~100之间的要求
5、模块
在Python中有一个概念叫做模块(module),这个和C语言中的头文件以及Java中的包很类似,比如在Python中要调用sqrt函数
,必须用import关键字引入math这个模块。
说的通俗点:模块就好比是工具包,要想使用这个工具包中的工具(就好比函数),就需要导入这个模块
导入模块的方式:
"""
第一种:直接导入整个模块,默认会执行模块中所有的动作
import 模块名
"""
import module01
import module02
# 调用的时候必须使用 模块名.方法名()/类名/全局变量 去调用
sum = module01.sum(10, 20)
print(sum)
module02.print_info("司马老贼")
"""
第二种:从模块里导入指定的 类名/方法名/全局变量
from 模块名 import 类名/方法名/全局变量
"""
from module01 import sum
print(sum(20, 30))
"""
第三种:从模块里导入所有的 类名/方法名/全局变量
from 模块名 import * 开发中不建议使用
"""
from module01 import *
6、模块中全局变量__name__的使用
print(globals())
"""
{'__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000273DBE7B080>,
'__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>,
'__file__': 'D:/Python/Python-workspace/Day10/module/module01.py', '__cached__': None}
"""
if __name__ == "__main__":
print("这是在本文件中的__name__")
"""
如果在本文件中执行代码,__name__的值代表的就是__main__这个字符串
如果这个文件作为模块被别的文件导入并且执行,则__name__代表的就是本文件的文件名
以上的if判断,多用于模块测试代码,用来进行自己运行时的测试并且不想让导入的人运行的代码块
通常的写法如下:
"""
def main():
# 此处是此时代码
pass
if __name__ == "__main__":
main()
7、模块中__all__的用法
"""
__all__=["函数名"]
这中写法表示只允许在列表中出现的函数名被导入
但是这种限制只作用于以 from 模块名 import * 这种导入方式
"""
8、包
Python中默认 模块/包 的位置:python安装路径\Lib
通过pip install安装的包的位置:python安装路径\Lib\site-packages