面向对象

面向过程编程与面向对象编程

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

  我们自然的习惯把具有相同特征的一类事物区分开来,所以在程序中也把具有相同的特征和技能的对象称为类,对象即是类中的具体一个。

类的定义

'''
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('汪汪汪...')

抽象类

在上面的动物类中,发出声响是具体一种动物的技能,我们不想在动物这个抽象的概念类中实现发出声响的代码,而是只想提供方法名,继承动物类都必须实现这个方法。
image

# 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.如果下个类存在两个合法的选择,选择第一个父类

继承的作用

继承减少了类的代码冗余,提高代码可读性。

多态

顾名思义,多态指事物有多种形态,如水可以是液、固、气三种状态。

image

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...
posted @ 2021-12-02 16:01  它叫鸮  阅读(52)  评论(0)    收藏  举报