面向过程并不适合开发复杂的项目 面向对象的特点 相比较函数,面向对象是更大的封装,根据职责在一个对象中封装多个方法
在完成某一个需求前,首先确定职责(需要做的事情)
根据职责确定不同的对象,在对象内部封装不同的方法(多个)
最后完成的代码,就是顺序让不同对象调用不同的方法
类是对一群具有相同特征或者行为的事务的一个统称, 是抽象的,不能直接使用
对象是由类创建的一个具体存在,可以直接使用,对象拥有该类的属性和方法
类和对象的关系就像图纸和飞机的关系,类就是一个模板,对象是根据这个模板创建出来的
类只有一个,对象可以由多个,不同对象的属性可能会有不同。
类中定义了什么属性和方法,对象中就有什么属性和方法。
面向对象开发前,需要进行需求分析 例如开发一个植物大战僵尸程序 在程序开发中,要设计一个类,通常需要满足下面三个要素
类名 这类事物的名字,满足大驼峰命名法则,每一个单词首字母大写,单词与单词之间没有下划线
属性 这类事物具有什么样的特征
方法 这类事物具有什么样的行为
名词提炼法 分析 整个业务流程,出现的名词,通常就是找到的类
对对象的特征描述,通常可以定义成属性
对象具有的行为(动词)通常可以定义为方法
需求中没有涉及的属性或者方法在设计类时,不需要考虑 练习1 需求
小明今年18岁,身高1.75,每天早上跑完步,会去吃东西
小美今年17岁,身高1.65,小美不跑步,小美喜欢吃东西 类:Person 属性:height,name,age 方法:run eat
目标:
dir内置函数
定义简单的类(只包含方法)
方法中self参数
初始化方法
内置方法和属性
dir内置函数(知道)
在python中对象几乎是无所不在的,我们之前学习的变量、数据和函数都是对象在python中可以使用以下两个方法验证:
在标识符/数据后输入一个.然后按下tab,就会提示可以调用的方法
使用内置函数dir传入标识符/数据,可以查看对象内所有属性和方法
提示__方法名__格式的方法时python提供的内置方法/属性,以下是几张常用的内置方法/属性
new 方法 创建对象时,会被自动调用
init 方法 对象被初始化时,会被自动调用
del 方法 对象被从内存中销毁前,会被自动调用
str 方法 返回对象的描述信息,print函数输出使用
利用好dir()函数,在学习时很多内容就不需要死记硬背了
定义一个简单的类(只包含方法) 例如
class 类名:
def 方法(self,参数列表):
pass
def 方法2(self,参数列表):
pass
方法的定义格式和之前学习过的函数一样
区别在于第一个参数必须是self, 创建对象格式如下 对象变量 = 类名()
第一个面向对象程序
需求:小猫爱吃鱼,小猫要喝水
分析:定义一个猫类Cat
class Cat:
def eat(self):
print("小猫爱吃鱼")
def drink(self):
print("小猫要喝水")
# 创建猫对象
tom = Cat()
tom.eat()
tom.drink()
在python中使用类创建对象之后,tom变量中仍然记录的是对象在内存中的地址
也就是tom变量引用了新建的猫对象
使用print输出对象变量,默认情况下,是能够输出这个变量引用的对象是由哪一个类创建的对象以及在内存章的地址(十六进制表示) 提示:在计算机中,通常使用十六进制表示内存地址
十进制 和十六进制都是用来表达数字的,只是表示的方式不一样
十进制和十六进制的数字之间可以来回转换%d输出十进制,%x可以16进制输出 再创建一个新的对象,分析两个对象的关系
class Cat:
def eat(self):
print("小猫爱吃鱼")
def drink(self):
print("小猫要喝水")
# 创建猫对象
tom = Cat()
tom.eat()
tom.drink()
print(tom)
# 在创建一个对象
lazy_cat = Cat()
lazy_cat.eat()
lazy_cat.drink()
print(lazy_cat)
lazy_cat2() = lazy_cat()#这两个猫是一个地址
方法中的self参数
案例改造--给对象增加属性
在python中,要给对象设置属性,非常的容易,但是不推荐使用
因为,对象属性的封装应该在类的内部
只需要在类的外部代码中直接通过.设置一个属性即可
class Cat:
def eat(self):#哪一个对象调用的方法,self就是哪一个对象的引用(地址)
print("小猫爱吃鱼")
def drink(self):
print("小猫要喝水")
# 创建猫对象
tom = Cat()
# 可以使用.属性名,利用赋值语句就可以
tom.name = "tom"
# 注意lazy_cat还是没有这个属性的
tom.eat()
tom.drink()
初始化方法
当使用类名()创建对象时,会自动执行以下操作:
为对象在内存中分配空间---创建对象
为对象的属性设置初始值--初始化方法(init)
这个初始化方法就是__init__方法,__init__是对象的内置方法
__init__方法就是专门用来定义一个类具有哪些属性的方法
class Cat:
def __init__(self):
print("这是一个初始化方法")
# 使用类名()创建对象的时候会自动调用初始化方法
tom = Cat()
在初始化方法内部定义属性
在__init__方法内部使用self.属性名 = 属性的初始值就可以定义属性
定义属性之后,在使用Cat类创建对象,都会拥有该属性。
class Cat:
def __init__(self):
print("这是一个初始化方法")
self.name = "tom"
# 使用类名()创建对象的时候会自动调用初始化方法
tom = Cat()
改造初始化方法--初始化的同时设置初始值 在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对__init__方法进行改造
把希望设置的属性值定义为__init__方法的参数
在方法内部使用self.属性 = 形参接受外部传递的参数
在创建对象时,使用类名(属性1,属性2,...)调用
class Cat:
def __init__(self,new_name):
print("这是一个初始化方法")
self.name = new_name
def eat(self):
print("%s 爱吃鱼" % self.name)
tom = Cat()
print(tom.name)
lazy_cat = Cat()
lazy_cat.eat()
内置方法的属性
__del__方法,对象从内存中销毁前,会被自动调用
当使用类名()创建对象时,为对象分配完空间后,自动调用__init__方法
当一个对象被内存中销毁前,会自动调用__del__方法
应用场景
__init__改造的初始化方法,可以让创建对象更加灵活
__del__如果希望在对象被销毁前,再做一些事情,可以考虑使用__del__方法
生命周期
一个对象从调用类名()创建,生命周期开始
一个对象的__del__方法一旦被调用,生命周期结束
在对象的生命周期内,可以访问对象的属性,或者让对象掉用方法
__str__方法,返回对象的描述信息,print函数输出使用
在python中,使用print输出对象变量,默认情况下,会输出这个变量引用的对象是由哪一个类创建的对象,以及在内存中的地址(十六进制表示)
如果在开发中,希望使用print输出对象变量时,能够打印自定义的内容,就可以利用__str__这个内置方法了
注意:__str__方法必须返回一个字符串
class Cat:
def __init__(self,new_name):
self.name = new_name
print("%s 来了" % self.name)
def __del__(self):
print("%s 我去了"% self.name)
def __str__(self):
#必须返回一个字符串
return "我是小猫 [%s]" % self.name
# tom是一个全局变量
tom = Cat("tom")
print(tom)
目标
封装
小明爱跑步
存放家具
封装
封装时面向对象的一大特点
面向对象编程的第一步--将属性和方法封装到一个抽象的类中
外界使用类创建对象,然后让对象调用方法
对象方法的细节,都被封装到类的内部
小明爱跑步
需求
小明体重75公斤
小明每次跑步会减肥0.5公斤
小明每次吃东西体重增加1公斤 类:Person 属性:name,weight 方法:init(self,name,weight); str(self);run(self);eat(self);
class Person:
def __init__(self,name,weight):
self.name = name
self.weight = weight
def __str__(self):
return "我的名字叫%s体重是%.2f 公斤" % (self.name,self.weight)
def run(self):
print("%s 爱跑步,跑步锻炼身体"%self.name)
self.weight -= 0.5
def eat(self):
print("%s 是吃货,吃完这吨再减肥"% self.name)
self.weight += 1
xiaoming = Person("小明",75.0)
xiaoming.run()
xiaoming.eat()
print(xiaoming)
扩展:小美也爱跑步
class Person:
def __init__(self,name,weight):
self.name = name
self.weight = weight
def __str__(self):
return "我的名字叫%s体重是%.2f 公斤" % (self.name,self.weight)
def run(self):
print("%s 爱跑步,跑步锻炼身体"%self.name)
self.weight -= 0.5
def eat(self):
print("%s 是吃货,吃完这吨再减肥"% self.name)
self.weight += 1
xiaoming = Person("小明",75.0)
xiaoming.run()
xiaoming.eat()
print(xiaoming)
# 小美爱跑步
xiaomei = Person("小美",45)
xiaomei.eat()
xiaomei.run()
print(xiaomei)
print(xiaoming)
提示:
在对象的方法内部,是可以直接访问对象的属性的
同一个类创建的多个对象之间,属性互不干扰
摆放家具
需求:
房子有户型、总面积和家具名称列表
新房子没有任何家具
家具有名字和占地面积,其中
席梦思,占地4平米
衣柜,占地2平米
餐桌,占地1.5平米
将以上三件家具添加到房子中
打印房子时,需求输出:户型、总面积、剩余面积、家具名称列表 剩余面积
在创建房子对象时,定义一个剩余面积属性,初始值和总面积相等
当调用add_item方法,向房间添加家具时,让剩余面积=家具面积 思考:应该先开发哪一个类 答案:家具类
家具简单
房子要使用家具,被使用的类,通常应该先开发
创建了一个房子类,使用__init__和__str__两个内置方法
准备了一个add_item方法准备添加家具
使用房子类创建了一个房子对象
让房子对象调用了三次add_item方法,将三件家具以实参传递到add_item内部 添加家具 需求
判断家具的面积是否超过剩余面积,如果超过,提示不能添加这个家具
将家具的名称追加打家具名称列表中
用房子的剩余面积-家具面积
class HouseItem:
def __init__(self,name,area):
self.name = name
self.area = area
def __str__(self):
return "[%s] 占地 %.2f" % (self.name,self.area)
class House:
def __init__(self,house_type,area):
self.house_type = house_type
self.area = area
#剩余面积
self.free_area = area
# 家具名称列表
self.item_list = []
def __str__(self):
# python 能够自动将一对括号内部的代码连接在一起
return ("户型:%s\n 总面积:%.2f[剩余:%.2f]\n家具:%s"
% (self.house_type,self.area,
self.free_area,self.item_list))
def add_item(self,item):
print("要添加 %s"% item)
# 1.判断家具的面积
if item.area > self.free_area:
print("%s 的面积太大了,无法添加" % item.name)
return #这样就不会执行下面的代码了
# 2.将家具的名称添加到列表中
self.item_list.append(item.name)
# 3.计算剩余面积
self.free_area -= item.area
# 1.创建家具
bed = HouseItem("席梦思",40)
# print(bed)
chest = HouseItem("衣柜",20)
# print(chest)
table = HouseItem("餐桌",1.5)
# print(table)
# 创建一个房子对象
my_home = House("两室一厅",60)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)
print(my_home)
目标
士兵突击案例
封装
封装时面向对象编程的一大特点
面向对象编程第一步--将属性和方法封装到一个抽象的类中
外界使用类创建对象,然后让对象调用方法
对象方法的细节都被封装在类的内部
一个对象的属性,可以是另外一个类创建的对象
士兵突击 需求
士兵许三多有一把ak47
士兵可以开火
枪可以发射子弹
枪装填子弹--增加子弹数量 先开发枪类,再开发士兵类 开发士兵类 假设:每一个新兵都没有枪 定义没有初始值的属性 在定义属性时,如果不知道设置什么初始值,可以设置为None
None关键字表示什么都没有
表示一个空对象,没有方法和属性,是一个特殊的常量
可以将None赋值给任何一个变量 fire方法需求
判断是否有枪,没有枪就没法冲锋
喊一声口号
装填子弹
射击
身份运算符
身份运算符用于比较两个对象的内存地址是否一致,是否是对同一个对象的引用
在python钟针对None比较时,建议使用is判断
应用场景及定义方式 应用场景
在实际开发中,对象的某些属性或方法可能只希望在对象的内部使用,而不希望在外部访问到
私有属性就是对象不希望公开的属性
私有方法就是对象不希望公开的方法 定义方法
在定义属性或方法时,在属性或者方法名称增加两个下划线,定义的就是私有属性或方法
python其实是没有真正意义上的私有属性方法的,实际是对名称做了一些特殊处理,使得外界无法访问
目标:
单继承
多继承 面向对象三大特性
封装根据职责将属性和方法封装到一个抽象的类中
继承实现代码的重用,相同的代码不需要重复的编写
多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度 继承的语法 class 类名(父类) pass
子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发
子类中应该根据需要,封装子类特有的属性和方法
class Animal:
def eat(self):
print("吃---")
def drink(self):
print("喝---")
def run(self):
print("跑---")
def sleep(self):
print("睡---")
class Dog(Animal):
def bark(self):
print("汪汪叫")
继承的专业术语
Dog类是Animal类的子类,Animal类是Dog类的父类,Dog类从Animal类继承
Dog类是Animal类的派生类,Animal类是Dog类的基类,Dog类从Animal类派生 继承的传递性
C类从B类继承,B类又从A类继承
那么C类就具有B类和A类的所有属性和方法 子类拥有父类以及父类的父类中封装的所有属性和方法 提问 哮天犬能够调用Cat类中定义的catch方法吗? 答案 不能,因为哮天犬和Cat之间没有继承关系
子类拥有父类的所有方法和属性
子类继承自父类,可以直接享受父类中以及那个封装好的方法,不需要再次开发 应用场景
当父类的方法实现不能满足子类需求时,可以对方法进行重写
class Animal:
def eat(self):
print("吃")
def run(self):
print("跑")
class Dog(Animal):
def bark(self):
print("汪汪")
class XiaoTianQuan(Dog):
def fly(self):
print("我会飞")
def bark(self):
# 1.针对子类特有的需求,编写代码
print("神一样的叫")
# 2.使用super(),调用原本在父类中封装的方法
super().bark()
# 3.增加其他子类的代码
print("fjslfjslfjlsfj")
xtq = XiaoTianQuan()
xtq.bark()
重写父类有两种情况:
覆盖父类的方法
如果在开发中,父类的方法实现和子类的方法实现完全不同
就可以使用覆盖的方式,在子类中重新编写父类的方法实现,具体的实现方法就是在子类中定义了一个和父类同名的方法并实现
重写之后,在运行时就只会调用子类中重写的方法,而不会调用父类封装的方法
对父类方法进行扩展
如果在开发中,子类的方法实现中包含父类的方法实现
父类原本封装的方法实现是子类方法的一部分
就可以使用扩展的方法
在子类中重写父类的方法
在需要的位置使用super().父类方法来调用父类方法的执行
代码其他位置针对子类的需求,编写子类特有的代码实现
关于super
在python中super是一个特殊的类
super()就是使用super类创建出来的对象
最常使用的场景就是在重写父类方法的时候,调用在父类封装的方法实现
子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法
子类对象可以通过父类的公有方法间接访问私有属性或私有方法
私有属性,方法是对象的隐私,不对外公开,外界以及子类都不能访问
私有属性,方法通常用于做一些内部的事情
class A:
def __init__(self):
self.num1 = 100
self.__num2 = 200
def __test(self):
print("私有方法 %d %d" % (self.num1,self.__num2))
def test(self):
print("父类的共有方法")
self.__test()
class B(A):
def demo(self):
# 1.在子类的对象方法中,不能访问父类的私有属性
print("访问父类的私有属性 %d" % self.__num2)
# 2.调用父类的私有方法
self.__test()
# 创建一个子类对象
b = B()
# 在外界访问父类的公有属性/方法
print(b.num1)
b.test()
print(b)
概念
子类可以有多个父类,并且荣有所有父类的属性和方法
例如:孩子会继承自己父亲和母亲的特性 语法 class 子类名(父类名1,父类名2...) pass
class A:
def test(self):
print("test方法")
class B:
def demo(self):
print("demo")
class C(A,B):
"""多继承可以让子类对象,同时具有多个父类的属性和方法"""
pass
# 创建子类对象
C = C()
C.test()
C.demo()
问题的提出
如果不同的父类中存在同名的方法,子类对象在调用方法时,会调用哪一个父类的方法呢?
提示:开发时,应该尽量避免这种容易发生混淆的情况,如果父类存在同名的属性或者方法时,应该尽量避免使用多继承
class A:
def test(self):
print("A---test方法")
def demo(self):
print("A---demo")
class B:
def test(self):
print("B---test方法")
def demo(self):
print("B---demo")
class C(A,B):
"""多继承可以让子类对象,同时具有多个父类的属性和方法"""
pass
# 创建子类对象
c = C()
c.test()
c.demo()
# 确定C类对象调用方法的顺序
print(C.__mro__)
A---test方法
A---demo
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
python针对类提供一个内置属性__mro__可以查看方法搜索顺序
MRO是method resolution order 主要用于在多继承时判断方法,属性的调用路径
在搜索方法时,是按照__mro__的输出结果从左至右的顺序查找的
如果在当前类中找到方法,就直接执行,不在搜索
如果没有找到,就查找下一个类中是否会有对应的方法,如果找到,就直接执行,不在搜索
如果找到最优一个类,还没有找到方法,程序报错
object是python为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir函数查看
新式类:以object为基类的类,推荐使用
经典类:不以object为基类的类,不推荐使用
在python3.x中定义类时,如果没有指定父类,会默认使用object作为该类的基类--python3.x中定义的类都是新式类
在python2.x中定义类时,如果没有指定父类,则不会以object作为基类
新式类和经典类在多继承时,会影响到方法的搜索顺序
为了保证编写的代码能够同时在python2.x和python3.x运行!今后在定义时,如果没有父类,建议统一继承自object class 类名 object: pass
多态 面向对象三大特性
封装根据职责将属性和方法封装到一个抽象的类中
定义类的准则
继承实现代码的复用,相同的代码不需要重复的编写
设计类的技巧
子类针对自己特有的需求,编写特定的代码
多态不同的子类对象调用相同的父类方法,产生不同的执行结果
多态可以增加代码的灵活度
以继承和重写父类方法为前提
是调用方法的技巧,不会影响到类的内部设计
在Dog类中封装方法game
普通狗只是简单的玩耍
定义xiaotianquan继承Dog,并且重写game方法
哮天犬需要在天上玩耍
定义Person类,并且封装一个和狗玩的方法
在方法内部,直接让狗对象调用game方法
class Dog(object):
def __init__(self,name):
self.name = name
def game(self):
print("%s蹦蹦跳跳的玩耍..."%self.name)
class XiaoTianDog(Dog):
def game(self):
print("%s 飞到天上去玩耍。。。"%self.name)
class Person(object):
def __init__(self,name):
self.name = name
def game_with_dog(self,dog):
print("%s和%s快乐的玩耍..." % (self.name,dog.name))
# 让狗玩耍
dog.game()
# 1.创建一个狗对象
wangcai = Dog("旺财")
wangcai2 = XiaoTianDog("飞天旺财")
# 2.创建一个小明对象
xiaoming = Person("小明")
# 3.让小明调用和狗玩的方法
xiaoming.game_with_dog(wangcai2)
理解正确,多态本质是对象的多态,而继承的本质是类的继承,前者是实现后者是抽象 多态的更主要的作用是在开发时只知道现在有DOG,不能预想到将来可能增加CAT,BIRD等等的时候 现在甚至可以直接写成object.game,这样在开发之初就保留了程序未来的扩展性
术语:实例
使用面向对象开发,第一步:设计类
使用类名()创建对象,创建对象的动作有两步
在内存中为对象分配空间
调用初始化方法__init__为对象初始化
对象创建后,内存中就有一个对象的实实在在的存在--实例 因此,通常会把
创建出来的对象叫做类的实例
创建对象的动作叫做实例化
对象的属性叫做实例属性
对象调用的方法叫做实例方法 在程序执行时:
对象各自拥有自己的实例属性
调用对象方法,可以通过self.
访问自己的属性
调用自己的方法 结论
每一个对象都有自己独立的内存空间,保存各自不同的属性
每个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部
python中一切都是对象
class AAA:定义的类属于类对象
obj1 = AAA()属于实例对象
在程序运行时,类同样会被加载到内存中
在python中,类是一个特殊的对象--类对象
在程序运行时,类对象在内存中只有一份,使用一个类就可以创建出很多个对象实例
除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法
类属性
类方法
通过类名.的方式可以访问类的属性或者调用类的方法
概念和使用
类属性就是给类对象中定义的属性
通常用来记录与这个类相关的特征
类属性不会用于记录具体对象的特征 实例需求
定义一个工具类
每件工具都有自己的name
需求--知道使用这个类,创建了多少个工具对象
class Tool(object):
# 使用赋值语句定义类属性,记录所有工具对象的数量
count = 0
def __init__(self,name):
self.name = name
# 让类属性的值+1
Tool.count += 1
# 1.创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")
# 2。 输出工具对象的总数
print(Tool.count)# 输出结果3
print(tool1.count)# 输出结果3
在python中属性的获取存在一个向上查找机制
因此,要访问属性有两种方式
类名.类属性
对象.类属性(不推荐)
类属性就是针对类对象定义的属性
使用赋值语句在class关键字下方可以定义类属性
类属性用于记录与这个类相关的特征
类方法就是针对类对象定义的方法
在类方法内部可以直接访问类属性,或者调用其他的类方法 语法如下: @classmethod def 类方法名(cls): pass
类方法需要用修饰器@classmethod来标识,告诉解释器这是一个类方法
类方法的第一个参数应该是cls
有哪一个类调用的方法,方法内的cls就是哪一个类的引用
这个参数和实例方法的第一个参数时self类似
提示:使用其他名称也可以,不过习惯使用cls
通过类名.调用类方法,调用方法时,不需要传递cls参数
在方法内部
可以通过cls.访问类的属性
也可以通过cls.调用其他类的方法
静态方法
在开发中,如果需要在类中封装一个方法,这个方法:
既不需要访问实例属性或者调用实例方法
也不需要访问类属性或者调用类方法
这个时候,就可以把这个方法封装成一个静态方法 语法如下: @staticmethod def 静态方法名() pass
静态方法需要用修饰器@staticmethod来标识,告诉解释器这是一个静态方法
通过类名.调用静态方法
需求
定义一个Game类
属性
定义一个类属性top_score记录游戏的历史最高分
定义一个实例属性player_name记录当前游戏的玩家姓名
方法:
静态方法show_help显示游戏帮助信息
类方法show_top_score显示历史最高分
实例方法start_game开始当前玩家的游戏
主程序步骤
查看帮助信息
查看历史最高分
创建游戏对象,开始游戏
class Game(object):
#历史最高分
top_score = 0
def __init__(self,player_name):
self.play_name = player_name
@staticmethod
def show_help():
print("帮助信息,让僵尸进入大门--僵尸")
@classmethod
def show_top_score(cls):
print("历史记录%d" % cls.top_score)
def start_game(self):
print("%s开始游戏啦..." %self.play_name)
# 1.查看游戏的帮助信息
Game.show_help()
# 2.查看历史最高分
Game.show_top_score()
# 3.创建游戏对象
game = Game("xiaoming")
game.start_game()
案例小结
实例方法--方法内部需要访问实例属性 实例方法内部可以使用类名.访问类属性
类方法--方法内部只需访问类属性
静态方法--方法内部,不需要访问实例属性和类属性 提问 如果方法内部急需要访问实例属性,有需要访问类属性,应该定义成什么方法 答案
应该定义成实例方法
因为,类只有一个,在实例方法内部可以使用类名.访问类属性
目标
单例设计模式
__new__方法
python中的单例
单例设计模式
设计模式
设计模式时前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案
使用设计模式是为了可重用代码,让代码更容易被他人理解,保证代码可靠性
单例设计模式
目的:--让类创建的对象,在系统中只有唯一的一个实例
每一次执行类名()返回的对象,内存地址是相同的
单例设计模式的应用场景
音乐播放对象
回收站对象
打印机对象
。。。
本文章使用limfx的vscode插件快速发布