面向对象
面向过程编程与面向对象编程
面向过程编程更像是工厂流水线的运作方式,一般思路清晰、逻辑严明、步骤明确,把握步骤后分步把逻辑写好即可,编程复杂程度低。但是面向过程的编程代码可扩展性差,往往牵一发而动全身。相反地,面向对象的编程可扩展性高,但是增加了编程的复杂程度,可控性差。

类
我们自然的习惯把具有相同特征的一类事物区分开来,所以在程序中也把具有相同的特征和技能的对象称为类,对象即是类中的具体一个。
类的定义
'''
class Dog():
'类的文档字符串'
类体
'''
# 类名首字母大写
class Dog():
role = 'dog' # Dog类属性都是狗
def brak(): # 狗都可以吠叫,一个类中的方法
print('dog is braking')
类的属性引用和实例化
class Dog():
role = 'dog' # Dog类属性都是狗
def __init__(self,name) # 类实例化对象默认把对象当成第一个参数传递给init方法
self.name = name
def brak(): # 狗都可以吠叫,一个类中的方法
print('dog is braking')
# 类名.属性名或函数名
print(Dog.role) # dog
print(Dog.brak()) # dog is braking
# 实例化对象: 变量名(对象名) = 类(参数)
dog1 = Dog('bro') # 类实例化对象执行Dog.__init()返回类似字典,存放属于这个对象的属性
# 对象名.属性名
print(dog1.name) # bro
print(dog1.role) # dog
# 对象名.方法名()
print(dog1.brak()) # dog is braking
对象
实例化的对象都拥有自己的属性,通过对象名.属性名查看,并且属性不存在时会到实例化它的类中查找,还是未找到即报错。对象调用类的方法和类相同,
class Dog():
role = 'dog'
def __init__(self,name)
self.name = name
def brak():
print('dog is braking')
dog1 = Dog('foo')
dog1.role = 'nothing' # dog1.role代表对象自身的属性,与类中role无关
print(dog1.role) # nothing
类与对象的命名名称空间
创建类会创建类的名称空间,用来存储类中定义的名字。这些名字称为属性,一般是类中的变量和函数。
实例化对象会创建对象的名称空间,用来存储对象定义的名字,并且这个对象绑定在类的名称空间。所以在自己名称空间中未找到的属性会再到类名称空间中查找。
绑定与非绑定方法
类中定义的方法有两种绑定方式,一、绑定对象,二、绑定类。
绑定对象的方法调用时将对象自生作为第一个参数传入,绑定类方法将类名作为第一个参数传入。
class Student():
def __init__(self,name):
self.name = name
# 绑定给对象
def study(self):
print('{} is studying...'.format(self.name))
stu1 = Student('xie')
stu1.study() # xie is studying
class Mysql():
def __init__(self,ip,port):
self.ip = ip
self.port = port
# 绑定给类
@classmethod
def get_mysql(cls):
return cls(settings.IP,settings.PORT)
mysql = Mysql.get_mysql()
非绑定方法既不绑定对象也不绑定类,此方法不需要对象和类名。
class Student():
school = 'SH'
def __init__(self, name, age):
self.name = name
self.age = age
@staticmethod # 静态方法
def create_id():
import uuid
return uuid.uuid4()
stu = Student('ly', 18)
print(stu.create_id())
隐藏属性
类的属性我们不应该能轻易的操作,就需要隐藏它。
class Student():
__school = 'mayu' # 通过 `__`隐藏类的属性
def __init__(self,name):
self.name = name
print(Student.__school) # 报错
此时,我们需要对外提供接口让对象访问和修改类属性。
class Student():
__school = 'mayu'
def __init__(self,name):
self.name = name
# 修改school
def set_school(self,school):
if not isinstance(school,str):
print('类型错误...')
return
self.__school = school
# 获得school
def get_school(self):
return self.__school
stu1 = Student('xie')
print(stu1.get_school()) # mayu
stu1.set_school('ruian')
print(stu1.get_school()) # ruian
其实,为类属性前加__只是python在类定义阶段发生了变体,此时school属性名在名称空间中是_Student__school,所有并不是不能通过类名.属性名访问。但是,既然我们为属性名加__就不要再直接访问属性了。对于属性的修改而言,也有了严格的控制,这便是隐藏属性的目的。
property
property装饰器装饰的函数能够当成属性直接访问,执行函数体代码,属性值为返回值。
class People():
def __init__(self,name,weight,height):
self.__name = name
self.weight = weight
self.height = height
@property
def name(self):
return 'name:{}'.format(self.__name)
@name.setter
def name(self,val):
if not isinstance(val, str):
print('类型错误...')
return
self.__name = val
@name.deleter
def name(self):
print('无法删除')
# def get_BMI(self):
# return self.weight / ( self.height ** 2 )
@property
def BMI(self):
return self.weight / ( self.height ** 2 )
p1 = People('xie',70, 1.8)
# print(p1.get_BMI()) # 21.604938...
print(p1.BMI) # 21.604938...
print(p1.name) # xie
p1.name = 'san'
print(p1.name) # san
del p1.name # 无法删除
继承
什么是继承
继承是创建新类的一种方式,新建的类可以继承一个或多个父类,父类也称基类或超类,新建的类可称为子类或派生类。
class Parent1:
pass
class Parent2:
pass
class Subclass1(Parent1):
pass
class Subclass2(Parent1,Parent2):
pass
# 查看所有继承的父类
print(Subclass1.__bases__) # (<class '__main__.Parent1'>,)
print(Subclass2.__bases__) # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)
# 如果没有继承父类,python默认继承object
print(Parent1.__bases__) # (<class 'object'>,)
如果有个动物类,这些动物都具有发出叫声这个技能,但是不同的动物发出的声音不同,我们可以通过super方法调用父类的方法。
class Animal:
def speak(self):
print('发出声音...')
class Cat(Animal):
def speak(self):
super().speak()
print('喵喵喵...')
class Dog(Animal):
def speak(self):
super().speak()
print('汪汪汪...')
抽象类
在上面的动物类中,发出声响是具体一种动物的技能,我们不想在动物这个抽象的概念类中实现发出声响的代码,而是只想提供方法名,继承动物类都必须实现这个方法。

# 1.方法一(通过abc模块实现)
import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def speak(self):pass
class Dog(Animal):
speak(self):
print('汪汪汪...')
# 1.方法二(通过手动报异常提醒开发者实现接口)
class Animal:
def speak(self):
raise NotImplementedError
class Dog(Animal):
def eat(self):
print('狗正在吃...')
dog1 = Dog()
dog1.speak() # 报错
继承中的属性查找顺序
# 单继承中属性查找顺序
# 先查找自身属性,在查找父类属性,在查找到对应的方法时,确认调用方法传递的参数对象。
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
obj = Bar()
obj.f2() # Foo.f2 Bar.f1
# 多继承中属性查找顺序(菱形继承)
# 新式类(python3):按照广度优先查询(根据继承顺序从左到右查询,如果继承的父类仍有父类递进查询,但是不查询最深层的父类,最后查询最深层的父类)
# 经典类(python2):按照深度优先查询(根据继承顺序从左到右查询,如果继承的父类仍有父类递进查询直到最深层)
class A(object):
def test(self):
print('from A')
class B(A):
def test(self):
print('from B')
class C(A):
# def test(self):
# print('from C')
pass
class D(B):
# def test(self):
# print('from D')
pass
class E(C):
# def test(self):
# print('from E')
pass
class F(D, E):
# def test(self):
# print('from F')
pass
f1 = F()
f1.test() # from B
那么继承究竟是如何实现的,其实对于你定义的类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有类的线性顺序。例如上面的F类,
print(F.mro()) # [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
python会根据MRO列表顺序来查找父类中的属性。MRO是基于C3线性算法实现的,它遵循3条准则:
1.子类会优先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果下个类存在两个合法的选择,选择第一个父类
继承的作用
继承减少了类的代码冗余,提高代码可读性。
多态
顾名思义,多态指事物有多种形态,如水可以是液、固、气三种状态。

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')
多态性
多态性指不考虑实例的情况下使用实例。
#只要是动物都有talk方法,不考虑他们具体类型直接使用
def func(obj):
obj.talk()
# 如python内置len方法
def len(obj):
return obj.__len__()
组合用法
假如有人类这个超类,还有老师和学生两个派生类。老师类需要有教授课程的列表,学生需要有学习的课程列表。
class People:
def __init__(self,name,age):
self.name = name
self.age = age
class Student(People):
def __init__(self,courses=None):
if not courses:
courses = []
self.courses = courses
class Teacher(People):
def __init__(self,courses=None):
if not courses:
courses = []
self.courses = courses
class Course:
def __init__(self,name,period,price):
self.name = name
self.period = period
self.price = price
python = Course('python','6mons',20000)
stu = Student('xie',18)
t = Teacher('lin',23)
stu.courses.append(python)
t.courses.append(python)
我们可以把课程抽离出来定义为一个类,给老师和学生对象一个courses属性存放Course类,这边是组合用法。
反射及内置方法
我们通过对象的内置方法查看及修改对象的属性。
class Dog:
def __init__(self,height,weight,gender):
self.height = height
self.weight = weight
self.gender = gender
def bark(self,sound):
print('{} wangwangwang...'.format(sound))
# getattr获取对象对应属性的属性值,没找到返回默认值(第三个参数)
dog = Dog(10, 10, 'male')
print(getattr(dog, 'height',5)) # 10
#setattr设置属性值
setattr(dog,'name','type')
print(dog.name) # type
# delattr删除属性值,未找到对应属性报错
delattr(dog,'height')
print(dog.height) # 报错
# hasattr判断是否存在此属性
print(hasattr(dog,'height')) # True
除了这些操作查看属性的方法,还有一些对象的内置方法,如__str__、__del__、__call__、__enter__、__exit__方法。
class Foo:
def __init__(self,m):
self.m = m
# 实现__str__方法打印该对象返回此方法返回的字符串
def __str__(self):
return 'print: {}'.format(m)
foo = Foo('xie')
print(foo) # print: xie
class Foo:
def __init__(self,m):
self.m = m
# 实现__del__方法该对象被销毁时调用此方法
def __del__(self):
print('回收了')
foo = Foo('xie')
# 终端打印回收了
class Foo:
def __init__(self, m):
self.m = m
# 实现__call__方法该对象加()调用时执行__call__方法
def __call__(self, *args, **kwargs):
print('from Foo')
foo = Foo('xie')
foo() # from Foo
class Open:
def __init__(self,name):
self.name = name
# 实现__enter__方法在碰到with(上下文管理器)时,执行此方法内代码
def __enter__(self):
print('open...')
# 实现__exit__方法在with代码块执行结束后执行此方法内代码
def __exit__(self, exc_type, exc_val, exc_tb):
print('close...')
with Open('a.txt') as f:
print('end...')
# open...
# end...
# close...

浙公网安备 33010602011771号