Loading

Python基础知识(三)

面向对象

  • 面向过程

    核心:"过程"
    过程的终极奥义就是将程序流程化,进而程序的设计变得简单化,扩展性差

    过程就是"流水线",用来分步骤解决问题

  • 面向对象

    核心:"对象"
    对象的终极奥义就是将程序"整合",提升程序的解耦合程度,设计复杂

    对象就是"容器",用来盛放数据与功能的

"""类即是类型;每一个类都可以当做一个新的数据类型。一切皆对象!!!"""
# 定义
	类是"容器",该容器用来存放同类对象公有的数据与功能;某种角度也可以称类为对象

# 语法
	class Student():
	    # 初始化对象独有的数据;
	    def __init__(self,name,age):
	        self.name = name # stu.__dict__['name'] = 'alan'
	        self.age = age  # 必须默认返回None
	
	    '''注意:return 在这个方法中不能有返回值'''
	
	    # 选课方法
	    def choose_course(stu_dict,course):
	        stu_dict['course'].append(course)
	        print(f"学生{stu_dict['name']}选课{course}")

# 定义类发生的事情
	立即执行类内代码
	产生一个类的名称空间,把类内执行的名字都丢到名称空间中(丢到大字典中)
	把类的名称空间绑定给__ dict __,类名.__dict__查看名称空间

对象(实例化)

# 先定义类,再调用类产生对象
	stu = Student('alan',18)  # 类体代码在定义阶段就运行了,实例化过程不会运行文件
	'''
	1.调用类产生一个空对象
	2.python自动调用了Student.__init__方法  <=>  传入(空对象,'alan',18)
	
	向对象中添加独有的属性
		stu.__dict__['gender'] = 'male' <=> stu.gender = 'male'
	'''

	# 查看类的名称空间
	print(Student.__dict__)
	# 查看对象的名称空间
	print(stu.__dict__)  # {'name': 'alan', 'age': 18}
	# 取值
	print(stu.__dict__['name'])
	print(stu.name)

属性的查找顺序与绑定方法

# 属性的查找:
	类属性:在类中的属性就称为类属性
	对象属性:在对象自己的名称空间中属性就是对象属性
	注意:
	属性的查找:先从自己的对象中查找,然后在去产生对象的类中取找
	如果使用dict从对象的名称空间获取值,找不到会报错
	使用.获取值,在自己的名称空间找不到会取类的名称空间查找
	
# 绑定方法
	绑定给谁就是在操作谁
	"""
	类的数据属性:是给所有对象用的,id都是一样的,一旦类发生改变,对象均跟着改变,反之对象其中之一改,其他均不变;
	类的函数属性:是绑定给对象用的,类调用位函数名,加括号直接调用;绑定给对象的方法,调用方法,将自己绑定方法,各不相同
	"""
	对象的绑定
		绑定给对象的方法,对象来调用,会把自己当成第一个参数传到函数里面self
	类的绑定
		@classmethod
		    def func(cls):
		        pass
		MyClass.func  # 类的调用,同样地,将类本身传进去,通常是需要再这方法中产生对象
# 静态方法
	''' 统计调用类,产生了多少个对象'''
	
	class Count_obj():
	    # 定义count
	    count = 0

	    def __init__(self):
	        Count_obj.count += 1

	静态方法/非绑定方法:既不绑定给类,也不绑定给对象
	    @staticmethod
	    def count_info():
	        print(f'产生了{Count_obj.count}个对象')
	    """没有绑定给任何人,类可以调,对象也能调,本质就是一个函数"""

property装饰器

# 引入BMI
	BMI指数为名词,但是要想得到BMI指数需要计算,所以需要将BMI伪装成数据属性
# property函数
	格式:
		property(fget=None, fset=None, fdel=None, doc=None)
		fget:获取属性值的方法
		fset:设置属性值的方法
		fdel:删除属性值的方法
		doc:属性描述信息
	示例:
		class Student:
		    def __init__(self):
		        self._age = None

		    def get_age(self):
		        print('查看属性时执行的代码')
		        return self._age

		    def set_age(self, age):
		        print('设置属性时执行的代码')
		        self._age = age

		    def del_age(self):
		        print('删除属性时执行的代码')
		        del self._age

		    age = property(get_age, set_age, del_age, '学生年龄')  # 将age伪装起来,看起来就像数据属性
		
		
		student = Student()
		# 查看属性的文档字符串
			Student.age.__doc__
		# 修改属性
			student.age = 18
		# 查看属性
			print(student.age)
		# 删除属性
			del student.age

封装

# 封装
	整合代码,将数据和功能整合到一起,起到规范代码的作用,更好的组织了项目的整体结构,减少了代码的冗余度,提升了可扩展性
# 隐藏属性
	如何隐藏
		在属性名前加__前缀,就实现对外隐藏的效果
	查看属性__dict__
		Foo._Foo__x
	特点
		该隐藏只是语法上的变形
	对外隐藏,对内不隐藏
		因为__开头的属性,会在类定义阶段时统一发生变形
		类内部也会发生变形,故造成了内部一起变形,不对内变形
	目的
		隐藏数据属性:
			将数据隐藏起来就限制了类外部对数据的直接操作,隐藏不是目的,目的是为了保护
		隐藏方法属性:
			目的是隔离复杂度,暴露给使用者简单的接口

继承

# 什么是继承
	继承是一种创建新类的方式,新建的类可称为子类或派生类,父类可称为基类或超类
	python支持多继承,新建的类可以支持一个或多个父类
# 基本格式
	class Parent1:
	    pass
	class Parent2:
	    pass
	class Sub1(Parent1): #单继承
	    pass
	print(Sub1.__bases__)  # 查看自己的父类---->(<class '__main__.Parent1'>,)

	class Sub2(Parent1,Parent2): # 多继承
	    pass

# 在py2中有经典类和新式类的区别

	新式类:继承了object类的子类,以及该子类的子类,子子类

	经典类:没有继承object类的子类,以及该子类的子类,子子类

	"""注意:在py3中没有继承任何类,默认继承object类,所以python3中都是新式类"""

# 解决de问题
	类解决对象与对象之间代码冗余的问题,子类可以遗传父类的属性
	继承解决的是类与类之间代码冗余的问题
	object类丰富了代码的功能
	继承的实现
		# 教师类
		class Teacher(Human):
		    def __init__(self, name, age, gender, level):
		        Human.__init__(self, name, age, gender)  # 调用函数self不能省略,部分功能重复父类
		        self.level = level

# 属性查找顺序
	"""对象>子类>父类>父父类"""
	单继承背景下属性查找
	
		class Foo():
		    def f1(self):
		        print('Foo.f1')
		    def f2(self):
		        print('Foo.f2')
		        self.f1()  # 此时的self为obj

		class Bar(Foo):
		    def f1(self):
		        print('Bar.f1')

		obj = Bar()
		obj.f2()
		# 结果
		Foo.f2
		Bar.f1
		'''查找顺序:
		1.obj先从obj名称空间找,再从Bar名称空间中找,没有f2去他爹(Foo)中找
		2.执行Foo中得f2,遇到self.f1()此时self是obj,是Bar的对象
		3.执行Bar中的f1
		'''
		
		# 区别下:父类不想让子类的方法覆盖,可以私有化
		class Foo:
		    def __f1(self):  # _Foo__f1()
		        print('Foo.f1')
		
		    def f2(self):
		        #
		        print('Foo.f2')
		        self.__f1()  # _Foo__f1()
		class Bar(Foo):
		    def __f1(self):  # # _Bar__f1()
		        print('Bar.f1')
		obj = Bar()
		obj.f2()
		# 结果
		Foo.f2
		Foo.f1
		'''Foo中f1私有化,所以输出的是Foo中的f1'''

# 菱形继承,mro列表
	例如
		class A():	# A为非object
		    def out_text(self):
		        print('from A')
		
		class B(A):
		    def out_text(self):
		        print('from B')

		class C(A):
		    def out_text(self):
		        print('from C')
		
		class D(B,C):
		    pass
		obj = D()
		obj.out_text() # 结果---->from B
		
		print(D.mro())
		# [<class '__main__.D'>,
		# <class '__main__.B'>, 
		# <class '__main__.C'>, 
		# <class '__main__.A'>,
		# <class 'object'>]  ==  mro列表,类已经该类产生的对象,查找顺序都是根据mro列表的顺序进行查找,mro列表是通过一个C3线性算法来实现的
	mro查找顺序
		子类先查,再查父类
		当继承多个父类的时候,按mro列表顺序被检查
		如果继承多个类,被继承类内具有相同的方法,先输出mro列表左边类的方法
	深度优先---"""python2中经典类,一条道走到黑"""
		class E:
		    pass
		class F:
		    pass
		class B(E):
		    pass
		class C(F):
		    pass
		class D:
		    def test(self):
		        print('from D')
		class A(B, C, D):
		    pass
		print(A.mro())
		'''
		查找顺序如下:
		[<class '__main__.A'>,
		<class '__main__.B'>,  # B分支
		<class '__main__.E'>,
		<class '__main__.C'>,  # C分支
		<class '__main__.F'>,
		<class '__main__.D'>,  # D分支
		<class 'object'>]
		'''
		
		obj = A()
		obj.test()
		# 结果为:from D
	广度优先---新式类
		class G:   # python3中默认继承object,python2中要将G继承object
		    def test(self):
		        print('from G')
		class E(G):
		    def test(self):
		        print('from E')
		class F(G):
		    def test(self):
		        print('from F')
		class B(E):
		    def test(self):
		        print('from B')
		class C(F):
		    def test(self):
		        print('from C')
		class D(G):
		    def test(self):
		        print('from D')
		
		class A(B, C, D):
		    pass
		obj = A()
		obj.test()  # 查找顺序为:obj->A->B->E->C->F->D-->G->object
		
		# 结果
		from B

# 多继承的优缺点
	优点
		子类可以同时遗传多个父类的属性,最大限度的重用代码
	缺点
		违反人的思维习惯,一个人有两个爹,代码的可读性会变差,不建议使用多继承,如果不可避免多个父类的继承,应该使用Mixins机制
	"""继承表达的是一种“是”什么关系"""

# Mixins机制
	多继承的正确打开方式:mixins机制
	mixins机制核心:就是在多继承背景下尽可能底提升多继承的可读性
	让多继承满足人的思维习惯--->什么“是”什么
		class Vehicle:
		    pass
		class FlyableMixin:   # 规范多继承,类似于常量;Mixin结尾、able结尾、ible结尾
		    def fly(self):
		        pass
		# 民航飞机
		class CiviAircraft(FlyableMixin,Vehicle)
		    pass
		#直升机
		class Helicopter(FlyableMixin,Vehicle)
		    pass
		class Car(Vehicle)
		    pass 
		
		'''表达是的关系,放在继承的末尾,特们都是交通工具'''
		
		'''eg:飞机有飞的功能,是交通工具'''

# 在子类中重用父类的功能
	方式一
		指名道姓调用某一类下的函数==》不依赖于继承关系
		People.__init__(self, name, age)
	方式二
		super()调用父类提供给自己的方法==》严格依赖继承关系
		super(当前类,  self).__init__(name, age, sex)  # python3可以简写,super().__init(name, age, sex),参照的是mro()
		
		class A:
		    def test(self):
		        print('from A')
		        super().test()  # 属性发起者的顺序查找,mro规定了A的父类为B
		'''用于调用下一个父类的方法B.test'''
		class B:
		    def test(self):
		        print('from B')
		class C(A, B):
		    pass
		c = C()
		c.test()
		print(C.mro())
		# 查找顺序如下
		#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

		# 结果
		from A
		from B

组合/类方法补充

# 定义
	一个类中以另外一个类的对象作为数据属性,就是类的组合,组合通常表示“有”的关系

# 目的
	类和类之间代码冗余的问题可以通过继承来解决,或者super()方法等,其实我们还可以通过组合解决类与类之间代码冗余的问题

# 类的方法补充:
	sel.__class__查看对象所属类
	类名/对象名.__dict__查看类/对象名称空间
	类名/对象名.__bases__查看父类
	起始类名.__mro__打印继承顺序,py3从左到右查找
	locals()查看局部名称空间
	globals()查看全局名称空间
	dirs(str)查看字符串所搭配的内置方法有哪些,查看内容可换

多态

# 什么是多态
	同一种事务有的多种形态
	eg:冰是水,水蒸汽也是水

# 多态性
	多态性是指可以不用考虑对象具体类型的情况下直接使用对象,多态性是同一个操作,作用到不同实例而表现出不同实现方式的特性
	通过派生,去定制子类特有的功能

# 多态性的优点
	增加的程序的扩展性,使得每次来一个实例化的对象,都是以同一种形式去调用的,多态性还增加了程序的可扩展性,通过继承父类,减少了代码的冗余

# 鸭子类型
	python推荐使用“鸭子类型”,是一种不依赖于继承,也可以实现不考虑对象类型而使用对象。

# 父类限制子类
	抽象类 (abc模块)
		class Animal(metaclass=abc.ABCMeta):  # 统一所有子类的方法
		    @abc.abstractmethod
		    def say(self):
		        pass
		下面但凡有子类继承了Animal父类的,必须含有say方法
	继承

类的内置方法

# 定义
	定义在类内部,__开头,__结尾,在满足某种条件下会"自动触发",不用自动主动调

# 如何使用
	__init__()
		初始化方法
	__str__()
		打印对象的时候,自动触发的函数
	__del__()
		在对象被删除的时候自动触发,程序运行结束即为对象被删除;在对象被删之前,应该回收操作系统的资源
	__call__()
		对象加括号自动触发,里面规定了__new__,__init__的执行顺序,比__new__还早
	__enter__()
		出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
	__exit__()
		with中代码块执行完毕时执行此方法
	__new__()
		早于init方法,造空对象

反射

# 什么是反射
	python是动态语言,而反射机制被视为动态语言的关键
		动态语言:在程序运行过程中,识别数据类型
	反射机制就是指程序运行过程中,动态获取程序信息以及动态调用对象的功能;--- "车到山前必有路"

# 为何用反射
	函数接受到一个参数之后,有可能会访问到这个参数内部的一些属性,但是又不确定该参数内部有无该属性,__dict__()虽然也可以获取,但是仍欠妥
	dir()  # 该函数是可以查看名下的属性、方法
# 如何实现反射
	dir()可以查看对象下所有的属性、方法;格式为["",...]  # 列表套字符串
	obj."name"  显然不行
	==》通过操作"字符串"进而操作属性
	obj.__dict__[dir(obj)[-1]]  # 并不是所有的数据都有__dict__属性
	"""引入四大反射内置函数"""
	hasattr(obj,pro):按pro判断是否有无obj.pro属性
	getattr(obj,pro,None):按pro判断是否有无obj.pro属性,没有返回None
	setattr(obj,pro,value):设置obj.pro的值相当于obj.pro = value
	delattr(obj,pro):删除obj.pro

元类

# 一切皆对象

# 类也是对象;元类就是用来实例化产生类的类

# 查看
	用class关键字定义的所有的类以及内置的类都是由元类type帮助我们实例化产生的
	print(type(int))

# calss机制
	类的三大特征
		类名 class_name
		类的基类 class_bases
		类体(字符串)  class_body
		
		exec(class_body, {}, class_dict)  # class_dict用来存放运行类体代码产生的名称空间
	调用基类*****  # 自定义的关键位置
		type(class_name, class_bases, class_dic)

# 自定义元类进而控制类的产生
	class People(A, B, metaclass=Mymeta):
	    """
	    注释信息
	    """
	    pass
	
	class Mymeta(type):  # 只有继承了type类的类才是元类
	    def __init__(self, class_name, class_bases, class_dic):
	        if not class_name.istitle():
	            raise NameError('类名首字母必须大写!')
	
	    def __new__(cls, *args):
	        return super().__new__(cls, *args)  # 会自动继承object父类
	
	    def __call__():  # 若想让对象加括号调用,则可以在对象里加括号调用
	        
	        return 
	
	People = Mymeta(class_name, class_bases, class_dic)  # 先造一个空对象People,再调用元类的Mymeta的__init__,完成初始化操作,最后返回值赋值给People
	    造空对象__new__(cls, (class_name, class_bases, class_dic))

posted @ 2022-07-31 17:01  爱learn  阅读(39)  评论(0编辑  收藏  举报