文章目录
一、面向对象基本概念
-
面向过程
-
传统的面向过程的编程思想总结起来就八个字——自顶向下,逐步细化!
→ 将要实现的功能描述为一个从开始到结束按部就班的连续的“步骤”
→ 依次逐步完成这些步骤,如果某一个步骤的难度较大,又可以将该步骤再次细化为若干个子步骤,以此类推,一直到结尾并得到我们想要的结果 -
程序的主体是函数,一个函数就是一个封装起来的模块,可以实现特定的功能,程序的各个子步骤也往往就是通过相关的函数来完成的!从而实现代码的重用与模块化编程
-
-
面向对象
- 所谓的面向对象,就是在编程的时候尽可能的去模拟现实世界!
在现实世界中,任何一个操作或者业务逻辑的实现都需要一个实体来完成!实体就是动作的支配者,没有实体,也就没有动作发生!
- 所谓的面向对象,就是在编程的时候尽可能的去模拟现实世界!
面向对象的三大特性:
- 封装:将属性和方法书写到类的里面的操作即为封装,封装可以为属性和方法添加私有权限。
- 继承:子类默认继承父类的所有属性和方法,与此同时子类也可以重写父类属性和方法。
- 多态:多态是同一类事物具有的多种形态。不同的对象调用同一个接口(方法),表现出不同的状态,称为多态。
二、类
2.1.类的基本定义
在python中也是构建类的, python3中支持经典类和新式类
- 经典类: 不由任意内置类型派生出的类, 称之为经典类
class 类名:
代码
......
- 新式类:
class 类名():
代码
......
这就是一个类,只不过里面什么都没有!其中,类名不区分大小写,遵守一般的标识符的命名规则(以字母、数字和下划线构成,并且不能以数字开头),一般为了和方法名相区分,类名的首字母一般大写!(大驼峰法)
案例: 定义一个 ‘人’ 类
class Person():
def eat(self):
print('我喜欢吃零食')
def drink(self):
print('我喜欢喝果汁')
实例化类:
格式:
对象名 = 类名()
案例: 实例化Person类, 生成p1对象
# 创建对象
p1 = Person()
# <__main__.Person object at 0x1013ecf50>
print(p1)
# p1对象调用实例方法
p1.eat()
p1.drink()
self 关键词: 在类中,有一个特殊关键字self,其指向类实例化对象本身。
# 1. 定义类
class Person():
def eat(self):
print('我喜欢吃零食')
# <__main__.Person object at 0x1058bced0>
print(self)
# 2. 创建对象
p1 = Person()
# <__main__.Person object at 0x1058bced0>
print(p1)
p1.eat()
p2 = Person()
# <__main__.Person object at 0x1058bcf50>
print(p2)
注意:打印对象和self得到的结果是一致的,都是当前对象的内存中存储地址。
2.2 类的添加和获取对象属性
属性即是特征,比如:人的姓名、年龄、身高、体重…都是对象的属性。
对象属性既可以在类外面添加和获取,也能在类里面添加和获取。
- 类外部添加对象属性:
# 定义类 class Person(): def print_info(self): print(f'姓名:{p1.name}') print(f'年龄:{p1.age}') print(f'地址:{p1.address}') print(f'地址:{p1.wife}') # 2. 创建对象 p1 = Person() # 3. 添加属性 print(f'姓名:{p1.name}') print(f'年龄:{p1.age}') print(f'地址:{p1.address}') print(f'地址:{p1.wife}') print("-----------") p1.print_info()
2.3. 魔术方法
在Python中,xxx()的函数叫魔法方法,指的是具有特殊功能的函数。
-
init()方法:
实例化对象时,连带其中的参数,会一并传给init函数自动执行它。init()函数的参数列表会在开头多出一项,它永远指代新建的那个实例对象,Python语法要求这个参数必须要有,名称为self(与java中的this有异曲同工之处)。class Person(): # 定义初始化函数 def __init__(self, name, age, address): # 添加实例属性 self.name = name self.age = age self.address =address def print_info(self): print(f"姓名:{self.name}, 年龄:{self.age},地址:{self.address}") p1 = Person('张三', 34, '北京') p1.print_info() p2 = Person('李四', 23, '上海') p2.print_info()
注意事项:
① init()方法,在创建一个对象时默认被调用,不需要手动调用
② init(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象
引用传递过去。 -
str()方法
当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__
方法,那么就会打印从在这个方法中 return 的数据。class Person(): # 定义初始化功能的函数 def __init__(self, name, age, address): # 添加实例属性 self.name = name self.age = age self.address = address def __str__(self): """ 返回一个对象的描述信息 """ return f'姓名:{self.name},年龄:{self.age},地址:{self.address}' p1 = Person('老王', 18, '北京') print(p1)
-
del()方法
当删除对象时,python解释器也会默认调用__del__()方法。
class Person(): # 定义初始化功能的函数 def __init__(self, name, age, address): # 添加实例属性 self.name = name self.age = age self.address = address def __del__(self): print(f'{self}对象已经被删除') p1 = Person('老王', 18, '北京') # <__main__.Person object at 0x101af8f90>对象已经被删除 del p1
2.4 私有属性和私有方法
在Python中,可以为实例属性和方法设置私有权限,即设置某个实例属性或实例方法不继承给子类。
设置私有属性和私有方法的方式非常简单:在属性名和方法名 前面 加上两个下划线 “__” 即可。
-
案例1:私有属性和私有方法设置方式
class Girl(): def __init__(self): self.name = '小美' self.__age = 18 def __showinfo(self): print('姓名:%s,年龄:%d' % (self.name, self.__age)) girl = Girl() print(girl.name) # 外界不能直接访问私有属性和私有方法 print(girl.__age) girl.__showinfo()
-
外部获取与设置私有属性值:
在Python中,一般定义函数名’ get_xx ‘用来获取私有属性,定义’ set_xx '用来修改私有属性值。class Girl(): def __init__(self): self.__name = '小美' self.__age = 18 def __showinfo(self): print('姓名:%s,年龄:%d' % (self.name, self.__age)) def get_name(self): return self.__name def set_name(self,name): self.__name = name def get_age(self): return self.__age def set_name(self,age): self.__age = age girl = Girl() print(girl.get_name()) print(girl.get_age()) # 外界不能直接访问私有属性和私有方法 print(girl.__showinfo)
2.5 类的单继承
2.5.1 概念和基本案例
概念说明:
继承:一个类从另一个已有的类获得其成员的相关特性,就叫作继承!
派生:从一个已有的类产生一个新的类,称为派生!
很显然,继承和派生其实就是从不同的方向来描述的相同的概念而已,本质上是一样的!
父类:也叫作基类,就是指已有被继承的类!
子类:也叫作派生类或扩展类
扩展:在子类中增加一些自己特有的特性,就叫作扩展,没有扩展,继承也就没有意义了!
单继承:一个类只能继承自一个其他的类,不能继承多个类,单继承也是大多数面向对象语言的特性!
多继承:一个类同时继承了多个父类, (C++、Python等语言都支持多继承)
案例:
class
def eat(self):
print('吃...')
def sleep(self):
print('睡...')
def call(self):
print('叫...')
class Dog(Animal):
pass
class Cat(Animal):
pass
重写父类属性和方法
2.5.2 重写父类的属性和方法
class Animal():
def __init__(self, name, age):
self.name = name
self.age = age
def sleep(self):
print("动物都是要睡觉的")
def eat(self):
print("动物都是要吃饭的")
class Dog(Animal):
def __init__(self, name, age, address):
super().__init__(name, age)
self.address = address
def eat(self):
# 希望在调用当前方法的时候, 先调用父类的方法
super().eat()
print("狗爱吃骨头")
class Cat(Animal):
def eat(self):
print("猫爱吃鱼")
class Cat(Animal):
def eat(self):
print("猫爱吃鱼")
# 构建子类对象
d1 = Dog('小爱', 3, '昌平区')
d1.eat()
d1.sleep()
print(d1.address)
c1 = Cat('小猫', 5)
c1.eat()
2.6 类的多继承
多继承:一个类同时继承了多个父类,并且同时具有所有父类的属性和方法例如:孩子会继承父亲 和 母亲的方法。
class Father(object):
pass
class Mother(object):
pass
class Child(Father, Mother):
pass
广度优先遍历:
从 H 开始往上搜索到 F, 若 F 没有数据, 则搜索和 F 同级的 G 里的数据, 若同级的 G 里还是没有数据,再继续往上搜索到 C, 若 C 还没有就再继续搜索到 A
python3的类继承都是按照广度优先来继承的
所以在Child类的上面的例子中,搜索顺序是[Child,Father, Mother,object]。 此顺序也称为Child类的线性化,用于查找此顺序的一组规则称为方法解析顺序(MRO)。
一个类的MRO可以被看作是 __mro__属性或者 mro() 方法。前者返回一个元组,而后者返回一个列表。
print(Child.__mro__)
print(Child.mro())
通俗的讲就是排在参数前面的父类会覆盖排在后面的父类的相同方法
2.7 多态
类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Animal 派生出 Cat和 Dog,并都写了一个 call() 方法,如下示例:
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
def call(self):
print(self.name, '会叫')
class Cat(Animal):
def __init__(self, name, age, sex):
super(Cat, self).__init__(name, age)
self.sex = sex
def call(self):
print(self.name, 'cat会“喵喵”叫')
class Dog(Animal):
def __init__(self, name, age, sex):
super(Dog, self).__init__(name, age)
self.sex = sex
def call(self):
print(self.name, 'dog会“汪汪”叫')
# 接下来, 定义一个do函数, 接收一个变量 'all', 如下:
def do(all):
all.call()
A = Animal('动物',4)
C = Cat('猫', 2, '男')
D = Dog('狗', 5, '女')
for x in (A,C,D):
do(x)
这种行为称为多态。也就是说,方法调用将作用在 all 的实际类型上。C 是 Cat 类型,它实际上拥有自己的 call() 方法以及从 Animal 继承的 call 方法,但调用 C .call() 总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。传递给函数 do(all) 的参数 all 不一定是 Animal 或 Animal 的子类型。任何数据类型的实例都可以,只要它有一个 call() 的方法即可。其他类不继承于 Animal,具备 call 方法也可以使用 do 函数。这就是动态语言,动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。
2.8 静态方法
静态方法需要用修饰器 “@staticmethod” 来标识,告诉解释器这是一个静态方法。
通过类名.调用 静态方法
class Game:
@staticmethod
def menu():
print('------')
print('开始[1]')
print('暂停[2]')
print('退出[3]')
Game.menu()
什么情况下,我们需要定义静态方法?
答:当某个方法既不需要调用自身属性也不需要调用自身方法,就可以将其封装为静态方法。
总结
python是一种弱类型的语言,但是实际上底层是一种强类型的语言。