面向对象
一、面向对象
面向对象是一种编程,以类的眼光来看待事物,将共同的属性和方法的事物封装到同一个类下面
- 封装:将有共同的属性和方法封装到同一个类下面
- 创建类和对象会分别创建二者的名称空间,我们能用类名.属性或发放,对象名.属性或方法的方式去访问里面的名字(属性或方法)
- 类中把某些属性和方法隐藏起来(定义为私有),只有类内部使用、外部无法访问,或留下少量接口供外部访问
- 继承:将多个类的共同属性和方法封装到一个父类下面,然后用这些类来继承这个类的属性和方法
- 多态:基类的同一个方法在不同的派生类中有着不同的功能
二、封装的那些事
隐藏属性(私有属性)
类里的属性或方法前加上两个下划线(__),代表属性或方法被隐藏,
class A:
__x = 1 # _A__x=1
def __init__(self, name):
self.__name = name # self._A__name=name
def __foo(self): # def _A__foo(self):
print('run foo')
def bar(self):
self.__foo() # self._A__foo()
print('from bar')
print(A.__dict__)
'''
{'__module__': '__main__', '_A__x': 1, '__init__': <function A.__init__ at 0x7fa2ed7c76a8>,
'_A__foo': <function A.__foo at 0x7fa2eeac7598>, 'bar': <function A.bar at 0x7fa2eeac7510>,
'__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
'''
python中的“私有”,实际上是在定义类的时候把“名字”给变形了
这种变形的特点:
- 在类外部无法直接obj.__AttrName
- 在类内部是可以直接使用:obj.__AttrName
- 子类无法覆盖父类__开头的属性
直接访问隐藏属性
A = A('haha')
print(A._A__x) # 1
A._A__foo() # run foo
- 通过变形后的名字访问属性
- 通过变形后的名字调用方法
问题:隐藏属性可以修改吗?
python没有做强制限制,实际上允许你修改,但不建议你修改,原本就是让你隐藏起来,你还故意这么操作。
封装数据属性的意义:明确的区分内外,控制外部对隐藏的属性的操作行为。
三、继承的那些事
-
继承的实现方式主要有两类:
- 实现继承:使用基类的属性和方法无需额外编码的能力
- 接口继承:仅使用属性和方法的名字,但是子类必须提供实现的能力(子类重构父类的方法)
-
python有两种类:
- 经典类:不继承object的类,object类里提供__str__等内置方法
- 新式类:继承object的类
- python3的类默认继承object,为新式类
- python2的类默认不继承object,为经典类,若主动继承object类,则为新式类
-
继承分为单继承和多继承
- python是支持多继承,(类名__.bases__ 查看当前类的父类)
- 继承的顺序可使用__mro__顺序查看
- 经典类继承顺序按深度优先查找
- 新式类继承查找顺序按广度优先查找
-
继承的方法查找:对象可以调用自己本类和父类的所有方法和属性, 先调用自己的 、自己没有 才调父类的。谁(对象)调用方法,方法中的 self 就指向谁
-
子类可以使用super(). 来使用父类的方法
实现继承
class Hero:
x = 3
def __init__(self, nickname, life_value, aggresivity):
self.nickname = nickname
self.life_value = life_value
self.aggresivity = aggresivity
def attack(self, enemy):
enemy.life_value -= self.aggresivity
class Garen(Hero):
pass
class Riven(Hero):
pass
g1 = Garen('德玛西亚', 29, 30)
接口继承(使用抽象类)
import abc
class Animal(metaclass=abc.ABCMeta): # 只能被继承,不能被实例化
all_type = 'animal'
@abc.abstractmethod
def run(self):
pass
@abc.abstractmethod
def eat(self):
pass
# animal=Animal() # TypeError: Can't instantiate abstract class Animal with abstract methods eat, run
class People(Animal):
def run(self):
print('people is running')
def eat(self):
print('people is eating')
class Pig(Animal):
def run(self):
print('people is walking')
def eat(self):
print('people is eating')
class Dog(Animal):
def run(self):
print('people is walking')
def eat(self):
print('people is eating')
peo1 = People()
pig1 = Pig()
dog1 = Dog()
四、多态的那些事
基类的同一个方法在不同的派生类中有着不同的功能
# 多态:同一类事物的多种形态
import abc
class Animal(metaclass=abc.ABCMeta): # 同一类事物:动物
@abc.abstractmethod
def talk(self):
pass
class People(Animal): # 动物的形态之一:人
def talk(self):
print('say hello')
class Dog(Animal): # 动物的形态之二:狗
def talk(self):
print('say wangwang')
class Pig(Animal): # 动物的形态之三:猪
def talk(self):
print('say aoao')
class Cat(Animal):
def talk(self):
print('say miamiao')
# 多态性:指的是可以在不考虑对象的类型的情况下而直接使用对象
peo1 = People()
dog1 = Dog()
pig1 = Pig()
cat1 = Cat()
鸭子类型
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子
在程序设计中是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。
鸭子类型在不使用继承的情况下使用了多态
class Duck:
def quack(self):
print("这鸭子正在嘎嘎叫")
def feathers(self):
print("这鸭子拥有白色和灰色的羽毛")
class Person:
def quack(self):
print("这人正在模仿鸭子")
def feathers(self):
print("这人在地上拿起1根羽毛然后给其他人看")
def in_the_forest(duck): # in_the_forest函数而言,对象是一个鸭子
duck.quack()
duck.feathers()
def game():
donald = Duck()
john = Person()
in_the_forest(donald)
in_the_forest(john)
game()
'''
这鸭子正在嘎嘎叫
这鸭子拥有白色和灰色的羽毛
这人正在模仿鸭子
这人在地上拿起1根羽毛然后给其他人看
'''