面向对象:继承、多态、封装
类、对象:
- 类:抽象的
- 对象:具体的
class Person: # 类名 def __init__(self, name, hp, aggr, gender): # __init__为初始化方法 self.name = name self.hp = hp self.aggr = aggr self.gender = gender def walk(self, n): # 定义方法,一般情况下必须传self参数,且必须写在第一个;后面还可以传其他参数,想传多少就多少 print("{}走了{}步".format(self.name, n)) p = Person("王者", "100", "5", "男") # 对象 = 类(参数),这个过程叫实例化 print(p.name) # 查看属性值 print(p.__dict__) # 查看所有属性 p.walk(10) # 调用这个对象拥有的方法
类有静态属性和方法,类中的静态属性和方法可以被对象和类调用。
class Person(object): money = 0 def work(self): Person.money += 100 mother = Person() father = Person() mother.work() father.work() print(Person.money) #200
类的组合:在一个类中以另外一个类的对象作为数据属性
class Car: """一次模拟汽车的简单尝试""" def __init__(self, make, model, year): self.make = make self.model = model self.year = year class Battery: """一次模拟电动汽车电瓶的简单尝试""" def __init__(self, battery_size=70): """初始化电瓶的属性""" self.battery_size = battery_size def describe_battery(self): """打印一条描述电瓶容量的消息""" print("This car has a " + str(self.battery_size) + "-kWh battery.") class ElectricCar(Car): """电动汽车特殊之处""" def __init__(self, make, model, year): """初始化父类的属性,再初始化电动汽车特有的属性""" super().__init__(make, model, year) self.battery = Battery() my_tesla = ElectricCar("tesla", "model s", 2016) my_tesla.battery.describe_battery() #This car has a 70-kWh battery.
面向对象三大特性:继承、多态、封装
继承
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又称为基类或超类,新建的类这时可称为子类或派生类
python中类的继承分为:单继承和多继承
一个类可以被多个类继承
一个类可以继承多个父类(python独有)
class A(object): # 定义父类 pass class B(object): # 定义父类 pass class C(A): # 单继承,父类是A,子类是C pass class D(A,B): # 多继承(python独有),用逗号分隔开多个继承的类 pass ########## 查看继承 ########## # __base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类 print(D.__base__) # <class '__main__.A'> print(D.__bases__) # (<class '__main__.A'>, <class '__main__.B'>) #在python3中,没有继承父类就默认继承object print(A.__bases__) # (<class 'object'>,)
派生
- 父类中没有的属性,在子类有,叫做派生属性
- 父类中没有的方法,在子类有,叫做派生方法
注意:
子类可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),一旦重新定义了自己的属性且与父类重名,调用时就以自己为准;
要是子类的对象调用属性,子类中有的名字就用子类的, 子类中没有才找父类的, 如果父类也没有就报错
在python3中,子类执行父类的方法也可以直接用super方法(super方法只在python3中存在)
class Base1(object): def func(self): print("Base1.func") class Base2(object): def func(self): print("Base2.func") class Foo(Base1, Base2): def func(self): # 方式一:根据mro的顺序执行方法 super().func() # Base1.func super(Foo, self).func() # Base1.func # 方式二:主动执行父类类的方法 Base2.func(self) # Base2.func print("Foo.func") obj = Foo() obj.func() # Foo.func super(Foo, obj).func() # Base1.func 在类外调用父类方法 print(Foo.mro()) # [<class '__main__.Foo'>, <class '__main__.Base1'>, <class '__main__.Base2'>, <class 'object'>]
继承顺序:
单继承:子类有的用子类,子类没有就用父类
多继承:
经典类中:深度优先
新式类中:广度优先
python2.7中: 经典类、新式类共存,新式类要继承object
python3中: 只有新式类,默认继承object
super的本质(python3才有,即只有新式类,广度优先):不是单纯找父类,而是根据调用者的节点位置的广度优先顺序来的
class A(object): def func(self): print("A") class B(A): def func(self): super().func() print("B") class C(A): def func(self): super().func() print("C") class D(B,C): def func(self): super().func() print("D") d = D() d.func() print(D.mro()) # mro()方法可查看类的继承顺序 # [<class "__main__.D">, <class "__main__.B">, <class "__main__.C">, <class "__main__.A">, <class "object">]
多态
多态指的是一类事物有多种形态
动物有多种形态:人、狗、猪
import abc class Animal(metaclass=abc.ABCMeta): # 动物类 @abc.abstractmethod def talk(self): pass class People(Animal): # 动物的形态之一:人 def talk(self): print("你好!") class Dog(Animal): # 动物的形态之二:狗 def talk(self): print("旺旺!") class Cat(Animal): # 动物的形态之三:猫 def talk(self): print("喵喵!")
# 类List与类Tuple都有相似的方法,那么这两个类就是鸭子类型,都可以使用len()方法,而不需要像其他语言一样告诉len()自己是什么类型 # 在强类型语言中,叫多态 # 在python中,叫鸭子类型 class List(object): def __len__(self): pass class Tuple(object): def __len__(self): pass def len(obj): return obj.__len__() l = List() # 或者T = Tuple() len(l)
封装
隐藏对象的属性和实现细节,仅对外提供公共访问方式。
好处:
1.将变化隔离;
2.便于使用;
3.提高复用性;
4.提高安全性。
封装原则:
1.将不需要对外提供的内容都隐藏起来;
2.把属性都隐藏,提供公共方法对其访问。
在python中用双下划线开头的方式将属性隐藏起来(设置成私有属性)
class Person: __country = "china" # 私有静态属性 def __init__(self, name, age): self.name = name self.__age = age # 私有实例属性 def __get_age(self): # 私有方法 return self.__age def func(self): return self.__get_age() # 调用私有xx的正确方式 p = Person("pd", 18) print(p.func()) # 正确使用方式 # print(p._Person__age) # 对象._类名__属性名(不建议这样使用!!!)
封装与扩展性
封装在于明确区分内外,使得类设计者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
class Person(object): def __init__(self, name, age): self.name = name self.__age = age def get_age(self): return self.__age def set_age(self, n): if 0 < n and n.isdigit(): self.__age = n else: return "年龄不合法" p = Person("pd", 18) print(p.get_age()) # 18 print(p.set_age(-18)) # 年龄不合法
会用到私有的这个概念的场景:
1.隐藏一个属性,不让类的外部直接调用
2.保护一个属性,不让属性随意被改变
3.保护一个属性,不被子类继承
小结
接口类、抽象类:
python中没有接口类,有抽象类,abc模块中的metaclass = ABCMeta,@abstructmethod
本质是做代码规范用的,希望在子类中实现和父类方法名字完全一样的方法
接口类与抽象类的区别:
在java的角度上看是有区别的
java没有多继承,所以为了接口隔离原则,设计了接口这个概念,以便实现多继承;
java本来就支持单继承,所以就有了抽象类。
python既支持单继承也支持多继承,所以对于接口类和抽象类的区别就没有那么明显;python中没有内置接口类
多态与鸭子类型:
多态 --> python天生支持多态
鸭子类型 --> 不依赖父类的情况下实现两个相似的类中的同名方法