Python基础6 面向对象
本节内容
面向对象编程介绍
为什么要面向对象开发?
面向对象的特性:封装,继承,多态
类,方法
引子
假设现在我们需要开发一款简单的游戏,譬如叫做人兽战争。我们需要简单的2个角色,一个人,一个怪兽,而且人和兽有不同的技能,不如人可以借助道具打怪兽,怪兽可以攻击人,咬人。
这时候我们可以写下如下的简单代码来描述这2个角色模型:
def person(name,age,sex,equipment):
data = {
"name": name,
"age": age,
"sex": sex,
"equipment": equipment
}
return data def monster(name, monster_type):
data = {
"name": name,
"monster": monster_type
}
return data
上述2个方法其实相当于造就了2个模型,游戏的开始,我们需要生成一个实际的人跟怪兽的对象。
m1 = monster("凯多", "兽人")
p1 = person("路飞", 20, "F", "海贼")
p2 = person("索隆", 21, "F", "剑豪")
角色对象生成了,我们需要考虑下一个问题,人跟兽有不同的功能,人打兽,兽咬人,等。这时候我们就需要再写不同的函数,想执行那个技能就去调用那个函数。
def bark(m):
print("monster [%s]: wow~ wow~" % m["name"]) def walk(p):
print("human [%s] is running" % p["name"]) m1 = monster("凯多", "兽人")
p1 = person("路飞", 20, "F", "海贼")
p2 = person("索隆", 21, "F", "剑豪") bark(m1)
walk(p1)
好了,我们已经基本实现了,所有最简单的功能了(这里就不一一执行简单的代码了)。
但是,在调用的时候,一不小心调用错了方法。
p2 = person("索隆", 21, "F", "剑豪")
bark(p2)
竟然把人的对象,传给怪兽的方法。事实上,其实这个没有错,但是从设计逻辑上来说确实不应该这样。如何实现这个限制呢?
def person(name, age, sex, equipment):
def walk(p):
print("human [%s] is running" % p["name"])
data = {
"name": name,
"age": age,
"sex": sex,
"equipment": equipment,
"walk": walk
}
return data def monster(name, monster_type):
def bark(m):
print("monster [%s]: wow~ wow~" % m["name"])
data = {
"name": name,
"monster": monster_type,
"bark": bark
}
return data m1 = monster("凯多", "兽人")
p1 = person("路飞", 20, "F", "海贼")
p2 = person("索隆", 21, "F", "剑豪") m1["bark"](p1)
如上,实现了这个限制,人只能用人技能。
但是我们只是组织了2个完全不同的角色之间功能的混用。但是有可能相同的角色,有些属性不一样呢?比如,海贼跟海军,因为都是人,我们需要建一个person()的模板。海军跟海贼可以相互打击,但是海军不可以杀死被绑架的人质,海贼可以。这些又怎么实现呢。也许你会说简单,只需要在杀人质的方法里面加上一个判断就可以。没错,这判断按解决了这个问题。但是你会发现海贼跟海军还有许多其他的区别,但是同事又有很多共性,如果每个都单独判断会累死。
也许你又会说,那既然这样,直接写2个角色,反正区别这么多。但是他们有很多共性啊,比如吃饭,走路。这样写就要很多相同点的一起重复书写了。
大概就是这么个意思。话题到这,我们引入了面向对象。
面向过程 VS 面向对象
编程范式
首先编程是用特定语法+数据结构+算法组成的代码告诉计算器如何执行任务的过程。一个程序是为了得到一个结果而编写的一组命令的集合。正所谓条条大路通罗马,实现一个任务的结果可以有多种多样不同的方式。对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。不同的编程范式本质上代表对各种各种类型的任务采取的不同的解决问题的思路,大多数语言只支持一种编程范式,当然也有语言可以同时支持多种编程范式。两种最重要的编程范式分别是面向过程编程和面向对象编程。
面向过程编程(Procedural Programming)
面向过程编程依赖 - 你猜到了- procedures,一个procedure包含一组要被进行计算的步骤, 面向过程又被称为top-down languages, 就是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。
举个例子,数据库备份,分三步,连接,备份,测试备份可用性。
代码如下:
def db_conn(): print("connecting db...")
def db_backup(dbname):
print("导出数据库...",dbname)
print("将备份文件打包,移至相应目录...") def db_backup_test():
print("将备份文件导入测试库,看导入是否成功") def main():
db_conn()
db_backup('my_db')
db_backup_test() if __name__ == '__main__':
main()
上述代码的问题也显而易见,如果你对程序进行修改,对你修改的部分有依赖或者调用功能的各个部分也要跟着一起修改。随着程序的越来越大,维护的难度不言而喻。
所以我们一般认为,如果只是用代码写一些简单的脚本,去做一些一次性的任务,用面向过程的方式是极好的,但是如果你要处理的任务是复杂的,切需要不断维护和迭代的,那面向对象就是最方便的了。
面向对象编程
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下
Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
Object 对象
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定。
面向对象编程(Object-Oriented Programming )介绍
OOP的三大特性:继承,封装,多态。
对于编程语言初学者来讲,OOP不是一个很容易理解的编程方式。大家素日按都知道三大特性,大家也都知道如何定义类,方法等面向对象的常用语法。但是一旦到真正操刀写程序的时候,很多时候还是喜欢用函数式编程来完成代码的书写。特别是初学者,很容易陷入一个窘境就是“我知道面向对象,我也会写类,但我依然没发现在使用了面向对象后,对我们的程序开发效率或其它方面带来什么好处,因为我使用函数编程就可以减少重复代码并做到程序可扩展了,为啥子还用面向对象?”。个人觉得,原因应该还是因为没有充分了解面向对象能带来的好处。
无论用什么形式编程,我们都要明确记住以下原则:
一、写重复代码是很不好的低级行为
二、代码是需要经常变更的
开发正规的程序跟写个运行一次久扔掉的小脚本是有很大的不同的。你的代码总是要进行不断修改的,不是修复bug就是需求的变更,增加。为了日后程序的修改更可扩展性,你写的代码一定要遵循易读易改的原则。
其实OOP编程的主要作用也是使你的代码修改和扩展变的更容易,那么小白要问了,既然函数都能实现这个需求了,还要OOP干毛线用呢? 呵呵,说这话就像,古时候,人们打仗杀人都用刀,后来出来了枪,它的主要功能跟刀一样,也是杀人,然后小白就问,既然刀能杀人了,那还要枪干毛线,哈哈,显而易见,因为枪能更好更快更容易的杀人。函数编程与OOP的主要区别就是OOP可以使程序更加容易扩展和易更改。
我们再写一个简单的警匪游戏的例子来引入吧。
比如先定义一个恐怖分子,一个警察:
#role 1
name = 'Alex'
role = 'terrorist'
weapon = 'AK47'
life_value = 100 #rolw 2
name2 = 'Jack'
role2 = 'police'
weapon2 = 'B22'
life_value2 = 100
这是一想,游戏一开始,任何一方把对面杀了就结束了,实在无趣,我们多加一些人进去。
#role 1
name = 'Alex'
role = 'terrorist'
weapon = 'AK47'
life_value = 100
money = 10000 #rolw 2
name2 = 'Jack'
role2 = 'police'
weapon2 = 'B22'
life_value2 = 100
money2 = 10000 #role 3
name3 = 'Rain'
role3 = 'terrorist'
weapon3 = 'C33'
life_value3 = 100
money3 = 10000 #rolw 4
name4 = 'Eric'
role4 = 'police'
weapon4 = 'B51'
life_value4 = 100
money4 = 10000
其实写到这边,小编已经写不下去了,硬撑一下,大家一定想到这样太散了,我们把这些家人字典中
roles = {
1:{'name':'Alex',
'role':'terrorist',
'weapon':'AK47',
'life_value': 100,
'money': 15000,
},
2:{'name':'Jack',
'role':'police',
'weapon':'B22',
'life_value': 100,
'money': 15000,
},
3:{'name':'Rain',
'role':'terrorist',
'weapon':'C33',
'life_value': 100,
'money': 15000,
},
4:{'name':'Eirc',
'role':'police',
'weapon':'B51',
'life_value': 100,
'money': 15000,
},
}
角色都出来了,再想想人物要是更多。。。实在是写不下去了。姑且吐槽到这边。开始面向对象的编程。
OOP:
class Role(object):
def __init__(self, name, age, weapon, life_v=100, money=1500):
self.name = name
self.age = age
self.weapon = weapon
self.life_v = life_v
self.money = money def shot(self):
print("shot...") def get_shot(self):
print("get shot...") def buy_gun(self, gun_name):
print("bought [%s]..." % gun_name) r1 = Role("dandy", "police" "M4A1")
r2 = Role("smith", "terrorist", "AK47")
# 生成了r1,r2两个角色
先不钻牛角尖,相比较靠函数拼拼凑凑出来的写法,可以看出上述的代码改进了两点:
1.代码量少了很多很多
2.角色和它具有的功能可以一目了然
上面运用到了类,下面我们就来解析一下类的基本定义与语法:
class human(object):
print("who am I?") H = human() # 这是对类的实例化
# 此时的H 就是human的实例化对象
# 实例化其实就是以human 类为模板,在内存里开辟一段内存空间,存上数据,赋值一个变量名
上面的代码其实太简单了,连最简单的传参都没有,继续:
class human(object): def __init__(self, name, sex):
self.name = name
self.sex = sex def sayname(self):
print("Hi, nice to meet you. My name is %s!" % self.name) H = human("dandy", "man")
H.sayname()
可能这个时候很懵。。。__init__是什么?self优势干嘛用?
首先先把最后的2句(实例化 & 调用方法)注释掉。
没实例直接打印human类输出如下:
# H = human("dandy", "man")
# H.sayname() print(human) output:
<class '__main__.human'>
这个输出代表什么?它的意思是,即使不实例化,这个human类本身也是存在于内存中的,那实例化后会怎么样呢?我引用一张图讲解下,例化时,会产生什么化学反应呢?
根据上图不难看出,其实self就是实例本身!实例化的过程中,python会自动把这个实例本身通过self参数传进去。
明白了类的基本定义,接下来分析下这块代码:
class Role(object): #定义一个类, class是定义类的语法,Role是类名,(object)是新式类的写法,必须这样写,以后再讲为什么 def __init__(self,name,role,weapon,life_v=100,money=15000): #初始化函数,在生成一个角色时要初始化的一些属性就填写在这里 self.name = name #__init__中的第一个参数self,和这里的self都 是什么意思? 看下面解释 self.role = role self.weapon = weapon
self.life_v = life_v
self.money = money
解释这个__init__,我们引入一个类的概念叫做,初始化方法也叫构造方法。在类被调用时,这个方法(虽然它是函数形式,但在类中就不叫函数了,叫方法)会自动执行,进行一些初始化的动作,所以我们这里写__init__(self,name,role,weapon,life_v=100,money=15000)就是要在创建一个角色时给它设置这些属性,那第一个self干嘛用?
初始化一个角色,就需要调用这个类一次:
r1 = Role("dandy", "police" "M4A1")#生成一个角色 , 会自动把参数传给Role下面的__init__(...)方法
r2 = Role("smith", "terrorist", "AK47")#生成一个角色
我们可以发现,上面创建角色的时候,其实并没有给__init__传值,程序也没有报错,是因为类在调用自己的__init__(....)自己帮你给self赋值了
1 r1 = Role("dandy", "police" "M4A1") #此时self 相当于 r1 , Role(r1,'dandy','police','M4A1’)
2 r2 = Role("smith", "terrorist", "AK47")#此时self 相当于 r2, Role(r2,'smith','terrorist','AK47’)
其实在执行r1 = Role("dandy", "police" "M4A1")时,python的解释器干了2件事:
1. 在内存中开辟一块空间指向r1这个变量名
2. 调用Role这个类并执行其中的__init__(…)方法,相当于Role.__init__(r1,'dandy','police',’M4A1’),这么做是为什么呢? 是为了把'dandy','police',’M4A1’这3个值跟刚开辟的r1关联起来 因为关联起来后,你就可以直接r1.name, r1.weapon 这样来调用啦。所以,为实现这种关联,在调用__init__方法时,就必须把r1这个变量也传进去,否则__init__不知道要把那3个参数跟谁关联呀。
所以这个__init__(…)方法里的,self.name = name , self.role = role 等等的意思就是要把这几个值 存到r1的内存空间里。
现在来解释为什么后面的几个方法还需要self参数呢?不是在初始化角色的时候,就已经把角色的属性跟r1绑定好了?
def buy_gun(self, gun_name):
print("bought [%s]..." % gun_name)
如果要调用类里面的方法,需要这样写:
r1 = Role("dandy", "police" "M4A1")
r1.buy_gun("AKW”) #python 会自动帮你转成 Role.buy_gun(r1,”B21")
执行结果:
bought [AKW]...
依然没给self传值 ,但Python还是会自动的帮你把r1 赋值给self这个参数, 为什么呢? 因为,你在buy_gun(..)方法中可能要访问r1的一些其它属性呀, 比如这里就访问 了r1的名字,怎么访问呢?你得告诉这个方法呀,于是就把r1传给了这个self参数,然后在buy_gun里调用 self.name 就相当于调用r1.name 啦,如果还想知道r1的生命值 有多少,直接写成self.life_v就可以了。 说白了就是在调用类中的一个方法时,你得告诉人家你是谁。
所以,根据以上,我们可以得出结论:
1. 上面的r1 = Role("dandy", "police" "M4A1")的过程,叫做类的实例化,就是把一个虚拟的抽象的类,通过这句代码变成一个具体的对象了这个对象叫做实例。
2. 刚刚定义的这个类体现了面向对象的第一个基本特征,封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容
面向对象的特性:
封装
封装,顾名思义,就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
封装你是面向对象的特征之一,是对象和类概念的主要特征。
继承
面向对象编程(OOP)语言的一个主要功能就是继承。继承是指这样的一种能力:它可以使用所继承父类的所以功能,并无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的类,我们称之为子类或派生类。 被继承的类称为基类,父类或超类。
继承的过程,就是从一般到特殊的过程。要实现继承,可以通过继承和组合来实现。
在某些OOP语言中,一个子类可以继承多个基类,但是一般情况下,一个子类只能有一个基类,要实现多重继承,用多级继承实现更好。
继承概念的实现方式主要有2类:实现继承、接口继承。
实现继承是指使用基类的属性和方法而无需编写额外的代码;
接口继承是指仅使用属性和方法的名称,但是子类必须提供实现的能力(子类重构父类方法);
在考虑使用继承时,有一点需要主要的是,2个类之间应该是属于的关系。例如,Boss是一个人,Manager也是一个人,他们都可以继承Person类,但是Head类确不能继承Person类,因为Head并不是一个人。
抽象类仅定义将由子类创建的一般属性和方法。
OO开发范式大致为:划分对象,抽象类,将类组织成为层次化结构(继承和合成),用类与实例进行设计和实现几个阶段。
继承示例代码
class SchoolMember(object):
member = 0 def __init__(self, name, age):
self.name = name
self.age = age def tell(self):
pass def enroll(self):
SchoolMember.member +=1
print("\033[31;1mnew member [%s] is enrolled, now there are [%s] members.\033[0m" % (self.name, SchoolMember.member)) def __del__(self):
"""析构方法"""
print("\033[32;1mmember [%s] is deleted !\033[0m" % self.name) class Teacher(SchoolMember):
def __init__(self, name, age, course, salary):
super(Teacher, self).__init__(name, age)
self.course = course
self.salary = salary
self.enroll() def teaching(self):
msg = "Hi, my name is [%s] and i will teach your [%s] course." % (self.name,self.course)
print(msg) def tell(self):
'''自我介绍方法'''
msg = '''Hi, my name is [%s], a [%s] teacher !''' % (self.name, self.course)
print(msg) class Student(SchoolMember):
def __init__(self, name, age, grade, id):
super(Student, self).__init__(name, age)
self.grade = grade
self.id = id
self.enroll() def tell(self):
msg = "Hi , I am [%s],I am studying [%s]." % (self.name, self.grade)
print(msg) if __name__ == "__main__":
t1 = Teacher("Dandy", 22, "Python", 20000)
t2 = Teacher("Rocky", 29, "C#", 19000) s1 = Student("Sam", 23, "Python", 1001)
s2 = Student("Tom", 24, "Python", 1002) t1.teaching()
t2.teaching()
t1.tell()
多态
多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单说,就是允许将子类类型的指针赋值给父类类型的指针。
那么,多态的作用是什么?我们知道封装可以隐藏代码实现的细节,使代码模块化;集成可以扩展已存在的代码模块;他们的目的都是为了--代码重用。而多态呢,是为了实现另一个目的--接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用其中任何一个类的实例的某一属性时的正确调用。
Pyhon 很多语法都是支持多态的,比如 len(),sorted(), 你给len传字符串就返回字符串的长度,传列表就返回列表长度。
多态代码示例
class Animal(object):
def __init__(self, name):
self.name = name def talk(self):
raise NotImplementedError("Subclass must implement abstract method") class Cat(Animal):
def talk(self):
print('%s: 喵喵喵!' % self.name) class Dog(Animal):
def talk(self):
print('%s: 汪!汪!汪!' % self.name) def func(obj): # 一个接口,多种形态
obj.talk() c1 = Cat('小晴')
d1 = Dog('李磊')
func(c1)
func(d1)
Python基础6 面向对象的更多相关文章
- Python 基础 四 面向对象杂谈
Python 基础 四 面向对象杂谈 一.isinstance(obj,cls) 与issubcalss(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls ...
- 自学Python之路-Python基础+模块+面向对象+函数
自学Python之路-Python基础+模块+面向对象+函数 自学Python之路[第一回]:初识Python 1.1 自学Python1.1-简介 1.2 自学Python1.2-环境的 ...
- 二十. Python基础(20)--面向对象的基础
二十. Python基础(20)--面向对象的基础 1 ● 类/对象/实例化 类:具有相同属性.和方法的一类人/事/物 对象(实例): 具体的某一个人/事/物 实例化: 用类创建对象的过程→类名(参数 ...
- python基础,函数,面向对象,模块练习
---恢复内容开始--- python基础,函数,面向对象,模块练习 1,简述python中基本数据类型中表示False的数据有哪些? # [] {} () None 0 2,位和字节的关系? # ...
- (转)Python成长之路【第九篇】:Python基础之面向对象
一.三大编程范式 正本清源一:有人说,函数式编程就是用函数编程-->错误1 编程范式即编程的方法论,标识一种编程风格 大家学习了基本的Python语法后,大家就可以写Python代码了,然后每个 ...
- Day7 - Python基础7 面向对象编程进阶
Python之路,Day7 - 面向对象编程进阶 本节内容: 面向对象高级语法部分 经典类vs新式类 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 作业:开发一个 ...
- Python基础7 面向对象编程进阶
本节内容: 面向对象高级语法部分 经典类vs新式类 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 作业:开发一个支持多用户在线的FTP程序 面向对象高级语法部分 经典 ...
- Python之路【第六篇】python基础 之面向对象(一)
一.三大编程范式 1.面向过程编程 2.函数式编程 3.面向对象编程 二.编程进化论 1.编程最开始就是无组织无结构,从简单控制流中按步写指令 2.从上述的指令中提取重复的代码块或逻辑,组织到一起(比 ...
- python基础之面向对象高级编程
面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个"函数"供使用(可以讲多函数中公用的变量封装到对象中) ...
- python基础(八)面向对象的基本概念
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 谢谢逆水寒龙,topmad和Liqing纠错 Python使用类(class)和对 ...
随机推荐
- django基于restframework的CBV封装
一.models数据库映射 from django.db import models # Create your models here. class Book(models.Model): titl ...
- POJ 2299树状数组求逆序对
求逆序对最常用的方法就是树状数组了,确实,树状数组是非常优秀的一种算法.在做POJ2299时,接触到了这个算法,理解起来还是有一定难度的,那么下面我就总结一下思路: 首先:因为题目中a[i]可以到99 ...
- 转:upload.parseRequest为空
上传是items一直是空list.导致原因是struts2把原始的原来S2为简化上传功能,把所有的enctype="multipart/form-data"表单做了wrapper最 ...
- HTTP协议相关原理
HTTP 的全称是 Hyper Text Transfer Protocol,中文名叫做超文本传输协议 HTTP协议是用于从网络传输超文本数据到本地浏览器的传送协议,它能保证传送高效而准确地传送超文本 ...
- nginx+keepalived高可用web负载均衡
一:安装环境 准备2台虚拟机,都安装好环境 centos 7keepalived:vip: 192.168.1.112192.168.1.110 nginxip 192.168.1.109 maste ...
- layui基础总结
1.layui结构 ├─css //css目录 │ │─modules //模块css目录(一般如果模块相对较大,我们会单独提取,比如下面三个:) │ │ ├─laydate ...
- consul介绍
consul 是一个支持多数据中心分布式高可用,用于服务发现和配置共享的工具. consul与其它工具的不同,官方介绍如下: https://www.consul.io/intro/vs/index. ...
- go context
Context 使用原则 1.不要把Context放在结构体中,要以参数的方式传递 2.以Context作为参数的函数方法,应该把Context作为第一个参数,放在第一位. 3.给一个函数方法传递Co ...
- SpringMvc的基本流程
1.流程图 2.特别说明 1)SpringMvc有6大组件(MVC各一个,再加3个运用策略模式的组件) 2)MVC对应的组件分别是(Handler.View.DisapatchServelet) 3) ...
- STM32学习笔记:【003】GPIO
版本:STM32F429 Hal库v1.10 在STM32中,最常用到的功能莫过于GPIO(General Purpose Input Output .通用输入/输出)了,在STM32中,除了除去AD ...