一、编程语言的发展:
机器语言 —> 汇编语言 —> 高级语言(面向过程的 C ) —> 面向对象(c++、Java、Python) 越高级,程序员就可以越偷懒
要把大象塞冰箱总共分几步?
二、面向过程:
1.先把冰箱打开;2.把大象塞进去;3.把冰箱关上
定义一个函数来实现功能:def {1.先把冰箱打开;2.把大象塞进去;3.把冰箱关上 }
往后遇到要实现把大象装冰箱这个操作时,直接调用函数就行
三、面向对象:
- 群体:“妈妈” —— 抽象 —— 类
- 个体:“李妈妈” —— 具体 —— 对象
对象: 所有你能看到的东西都是对象。对象是具有 属性 和 方法 的实物,(桌子、水杯、电灯、墙…都是对象),
-
属性:
是静态的名词性的描述一个东西,比如说:电扇这个对象的属性有 高度、重量、颜色、风力大小、额定电压 等。 -
方法:
是指对象的功能和行为,风扇的功能能就是吹风。
四、类:具有相同 “属性” 和 “方法” 的抽象
一个抽象的名词,没有特征,没有特指,模糊的东西,就叫 类,比如说:桌子,你不知道哪一个桌子,不知道什么样的桌子
类是对一系列事物的统称,同类事物必定具有相同的特征
特征分类:
- 状态(静态化) —— 变量 / 属性(成员变量)
- 行为(动态化) —— 方法(成员方法)
统称:类的成员
类的定义:
# 定义格式一:
class 类名:
pass
# 定义格式二:
class 类名:
成员变量 >>> 静态化的状态 年纪 国籍 身高 体重
成员方法 >>> 动态化的行为 函数
1、成员变量:
# 定义格式一:
class 类名:
“”“成员变量 >>> 静态化的状态 ”“”
def __init__(self): # 定义成员的变量 固定的语法格式
self.变量名1 = 值1 # 成员变量
self.变量名2 = None # 并不确定该属性具体的值
>>> 成员方法 —— 动态化的行为 函数
# 调用格式:
取值:对象名.变量名
赋值:对象名.变量名 = 值
# 创建对象
变量名 = 类名() # 对象名
print(变量名.方法)
# 定义格式二:
对象名.变量名 = 值 # 创建对象出来之后,属于该对象独有的属性/变量
# 调用格式
取值:对象名.变量名
赋值:对象名.变量名 = 值
2、成员方法:
# 定义格式一:
class 类名:
def 方法名(self):
方法体
# 调用格式
对象名.方法名()
self 仅在声明时占位,无需传递具体参数
# 定义格式二:
class 类名:
def 方法名(self,形参1,形参2,......):
方法体
# 调用格式一:
对象名.方法名(实参1,实参2,......)
怎么表示类呢
class person(): # 类
def __init__(self, name, age, sex, heigh): # 属性写括号里
self.name = name # self 的含义代表即将要出现的面向对象
self.age = age
self.sex = sex
self.heigh = heigh
def say(self):
print(self.name, self.age, self.sex, self.heigh, "hello")
p = person("name", 18, "男",180) # 运行 __init__ 方法
p.say()
(1)属性(这个类的一些名词属性):
上面定义了属性(name、age、sex、height),又定义了方法say(),这样就构成了一个类,但是类是一个抽象的概念,你想让“人说话”,就得把抽象的类实例化具体哪个人说话,那么下面的p就是一个对象,p = person(),但是就这样一个模糊的人,你不知道它具体长啥样,于是需要在括号里填属性:p = person(“name”,18, “男”, 180),这样创建一个人就能具体实实在在知道他是谁,然后就可以运行功能p.say(),我想让他做自我介绍,然后就是在say() 方法里面设置属性:
def say()
print(self.name, self.age, self.sex, self.height, "hello")
所以,创建对象之前,得要有一个类,一个概念性的东西,抽象的类就是一个造汽车的图纸,只有根据图纸造出具体的对象汽车了,才可以使用汽车的一些方法
__init__ 叫:构造函数,构造啥用的呢?构造 对象p 用的。
只要你写一个类名加一个括号,这个构造函数__init__是自动调用的,只要创建了对象,那么类里面的构造方法就会被自动调用,我们也把__init__叫做 魔法方法
然后构造函数下面出现一系列的 self属性,self 的含义代表即将要出现的那个对象p,self 指向的就是p,临时指向新创建的对象(等具体的新对象p来接手)
如下:如果你临时创建的对象没有东西指向它的话,系统会认为它是一个垃圾,所以需要self临时指向一下这一串内存地址,有了一个名字,这时候它就不是垃圾了
但最后还得回到具体的新对象p去指向,因为当你再创建其它新对象了,self就又得去指向新的对象了
整体捋一遍:
问:构造函数里有没有return?
init 方法 可以有return,但return必须是 return None,其实返回None, 最终接收的还是 self 对象;
(理论上说应该返回self,这样才能把self的值赋值给P,这样说理论是对的,但是实际上一般在init方法里不写return)
或者说 没有 return,但是构造函数init方法会给返回一个值,返回一个 临时对象self 赋给 新创建的对象p
(2)方法 —— 这 个 类 所 具 有 的 功 能 :
静态方法:
如上图,
静态方法是不依赖对象的,通过类,就可以使用这个方法;
say()方法 和 静态方法run() 的存储空间是不一样的,前者say()基于 对象,后者run()基于 类,
所以 run( ) 通过 类 来调用, say( ) 通过 对象 来调用,静态方法 run( ) 也可以通过对象来调,但是得 传对象,
看一下两种方法的内存模型是怎么样的:
当然,静态方法也可以用对象来调用的:p.run()
如下:
下图 为 对象直接调用静态方法,调用时少了四个参数,
下图 为 传完对象后 的调用成功:
所以,
类方法 只能 类 来调用,
静态方法 是 类 和 对象 都可以调用
对象:对象 通过类名 后加一个 括号 的形式 来创建,创建后 里面的一个内存模型,怎么赋值的要知道
str 魔术方法的介绍
class man:
def __init__(self):
self.gender = '男性'
self.age = None
# 成员方法 定义部分
def sing(self,num1):
print('唱了%s首歌' % num1)
def dance(self,num2):
print('跳了%s只舞' % num2)
# def __str__(self):
# return一个返回值
# return '这个类是创建人类的末班'
man1 = man()
print(man1)
class man:
def __init__(self):
self.gender = '男性'
self.age = None
# 成员方法 定义部分
def sing(self,num1):
print('唱了%s首歌' % num1)
def dance(self,num2):
print('跳了%s只舞' % num2)
def __str__(self):
# return一个返回值
return '这个类是创建人类的末班'
man1 = man()
print(man1)
str魔术方法,特定的时机去进行调用,在使用print函数打印对象的时候,会自动执行代码,也属于魔术方法
# 定义格式:
def __str__(self):
return '显示的信息'
五、面向对象的三大特性:
- 继承
- 封装
- 多态
1、继承(涉及到好几个类的关系处理)
优点:减少代码量,把一些重复的、共用的代码提出去,但并不是这个共同部分就归谁所有,只是调用共同代码的使用权,
缺点:耦合程度太高(比如,下述继承案例中:cat 如果不和 animal 建立联系的话,那么 cat类 就什么用都没有,就是一个废类,那咱们写代码的时候,要尽可能的减低代码类与类之间的耦合程度------> 高内聚、低耦合 )
-
构造函数:没有显示和声明时,系统会自动给
-
子类使用父类属性时,必须手动调用父类构造函数
-
重写和覆盖:当父类方法不能满足子类的需要时,则在每个子类中都要覆盖,
比如:
猫也要吃,狗也要吃,但猫吃猫粮,狗吃狗粮,它不一样;
如果吃的方法一样,可以继承父类,吃的方法不一样,那就只能覆盖了
# 这是源代码:
class cat():
def __init__(self,name,weight,age):
self.name = name
self.age = age
self.weight = weight
def eat(self):
print("吃饭")
def run(self):
print("跑步")
def sleep(self):
print("睡觉")
class dog():
def __init__(self,name,weight,age):
self.name = name
self.age = age
self.weight = weight
def eat(self):
print("吃饭")
def run(self):
print("跑步")
def sleep(self):
print("睡觉")
---------------------------------------------------
# 这是采用继承后的代码:
class animal():
def __init__(self, name, weight, age):
self.name = name
self.age = age
self.weight = weight
def eat(self): #下面的吃饭、跑步、睡觉,这三个方法是父类的方法,这些父类的方法想要调用得通过 animal父类 的对象来调用
print("吃饭")
def run(self):
print("跑步")
def sleep(self):
print("睡觉")
class cat(animal): # cat是子类,animal是父类
# 构 造 函 数 在 没 有 写 上 显 示 声 明 时 ,系 统 会 默 认 提 供 ----> def __init__ (self):
pass
class dog(animal):
pass
c = cat()
d = dog()
c.sleep() # 子类对象进行调用父类方法
d.eat()
小实验:
验证 父类对象到底创没创建 ,如果创建了,那么 print(“这是父类构造函数”) 一定会走
只需要看看父类构造函数能不能运行出来,如果运行出来了,那说明它底层给咱们构造了一个函数,只是咱们没看见
结果,没有走父类
下图所示,父类构造函数没有运行出来,那是怎么调用父类的呢
怎么创建父类对象呢?
下面把属性打开:
子类使用父类属性时,必须手动调用父类构造函数:
这种 手动调用的方法是绝对没有问题的,括号里写三个参数,属性
调用父类属性的时候,使用的是父类本身的属性,而不是父类属性的复印版(如下图 id 验证问题:调用的属性是父类本身)
重写和覆盖:
当父类方法不能满足子类的需要时,则在每个子类中都要覆盖,比如:猫也要吃,狗也要吃,但猫吃猫粮,狗吃狗粮,它不一样;如果吃的方法一样,可以继承父类,吃的方法不一样,那就只能覆盖了
class animal():
def __init__(self, name, weight, age):
self.name = name
self.age = age
self.weight = weight
print(id(self.name))
def eat(self):
print("吃饭")
def run(self):
print("跑步")
def sleep(self):
print("睡觉")
class cat(animal):
def __init__(self):
super().__init__("小花",2,2) # super() 手动调用父类init方法
print("这是子类构造函数")
def eat(self):
print("猫粮")
pass
c = cat()
c.eat()
多继承的继承顺序是:
先找本身(子类)—> 再找继承的父类1(亲父类) —> 再找继承的父类2 —> 再找继承的父类3 (若父类没了!---->就找爷爷(父类的父类))===>要是找一圈都没有这个方法,就会报错了!
【二叉树:从左到右,一层一层找,这个叫:广度优先】
class father0():
def __init__(self):
print("这是亲爹")
def fun0(self):
print("功能1:当大官")
class father1():
def __init__(self):
print("这是干爹1号")
def fun1(self):
print("功能2:变有钱")
class father2():
def __init__(self):
print("这是干爹2号")
def fun2(self):
print("功能3:找媳妇")
class son(father0,father1,father2):
pass
s =son()
s.fun0()
s.fun1()
s.fun2()
六、字符串练习
1.字符串解析,现有一字符串,“卡巴斯基#杀毒软件#免费版#俄罗斯#”,解析出每个元素
str = "卡巴斯基#杀毒软件#免费版#俄罗斯#"
str = str[:-1] # -1表示最后一个元素的下标 || 也可以用:str = str[:len(str)-1]
print(str.split("#")) #考察的方法是 split()
2.“那车水马龙的人世间,那样地来 那样地去,太匆忙”最后一次出现“那”的位置。
str = "那车水马龙的人世间,那样地来 那样地去,太匆忙"
index = str.rfind("那")
print(index)
3.判断输入的字符串是否是.py结束
str = input("请输入一个字符串:")
if str[-3:]==".py":
print(True)
else:
print(False)
4.有一个身份证号码,判断此为男还是女,基于此方法,写一个算法,判断一个身份证号为男还是女。(身份证号分15位和18位)
def is_man(idcard):
if len(idcard)==18:
return True if int(idcard[-2])%2 !=0 else False #身份证号倒数第二位奇数是男性,偶数位女性
if len(idcard)==15:
return True if int(idcard[-1])%2 !=0 else False #身份证号倒数第一位奇数是男性,偶数位女性
b = is_man("420984199802048416")
print(b)
18位身份证,看倒数第二位,为奇是男性
15位身份证,看倒数第一位,为奇是男性
5.有如下格式的字符串name-age-sex-address,解析出姓名,年龄等信息
str = "name-age-sex-address"
print(str.split("-"))
6.求出字符串中有多少种字符,以及每个字符的个数
static void printCharInfo(String str)
例如有字符串 str=“apple is a apple.”:
结果应该是:
(考察的点在排序,先转列表再排序)
字符串转列表:list = [str[i] for i in s]
s = "apple is a apple."
list = [str(i) for i in s]
list.sort() # 排完序
print(list)
count = 0
a = list[0]
for item in list:
if item==a:
count+=1
else:
print(a,":",count)
count = 1
a = item
print(a,":",count)
七、总结:
1,面向过程 和 面向对象
class 类名():
类的体(属性和方法)
2,创建对象
引用(变量) = 类名()
__init__(self) 构造函数
3,方法:类方法 对象方法
4,面向对象的三大特性:继承 封装 多态
class 子类/派生类(父类/超类/基类):
class A(object):
def a():
pass
pass
class B():
def b():
pass
pass
class C(B) # C把B继承过来了,C也可以同时有自己的方法
class D(A,B,C): # 多继承,D 把 A、B 、C都继承过来了
def b():
从左到右,整体深度优先 局部广度优先(有菱形结构)
覆盖/重写