Python面向对象编程


目录


  1. 面向过程与面向对象区别
  2. 面向对象
  3. 对象的三个重要属性
  4. 类与对象的基本概念及联系
  5. 定义类、构造函数(构造方法)__init __
  6. 继承
  7. 抽象类与接口
  8. 组合
  9. 鸭子类型与多态
  10. 封装
  11. 三大装饰器@property、@classmethod、@staticmethod

一、面向过程与面向对象

项目面向过程面向对象
编程方法自顶而下自底而上
代码主体结构程序=数据(变量)+算法(函数/过程)程序=对象+交互
数据操作主体由函数/过程进行加工或展现在对象的方法中加工或展现
模拟方法通过函数/过程操纵表现世界的数据与状态把世界描绘成具有主动性的对象进行交互(上帝视角)
编程思想代码实现处理数据的步骤面向对象分析
优点降低程序复杂度解决了程序扩展性问题

二、面向对象

理解对象:对象是具有自身的特征或能力
在计算机中,表现为对象具有解决问题所需的特征和能力(数据属性和方法属性)

三、对象的三个重要属性

Python面向对象编程:python中万物皆对象,每个对象都有三个属性id、type(类型)、value(值)
id:指向对象的地址(可以理解为内存中的位置)
is:身份运算符,
注意:id相同,则value一定相同;id不同,value可能相同

# 基于Python 3.7 IDLE
>>> a=1
>>> print(id(a))  #a的内存地址
1905867008
>>> print(type(a))  #a的类型
<class 'int'>
>>> print(a)
1
>>> b=2
>>> print(id(b))
1905867024
>>> print(type(b))
<class 'int'>

>>> print(a is b)  #a和b的id(地址)不同
False
>>> print(a==b)  #a和b的value(值)不同
False
>>> c=1
>>> print(id(c))
1905867008
>>> print(a is c)  #a和c的id(地址)相同
True
>>> print(a==c)  #a和c的value(值)相同
True
>>> x=500
>>> y=500
>>> print(x is y)  #x和y的id(地址不同)可以尝试打印id
False
//ending

猜想:可能跟值的大小有关

四、类与对象的基本概念及联系

类(class)与对象(object)是面向对象的核心概念。
类是对某一类事物的描述,是抽象的概念上的定义;对象是实际存在的属于该类事物的具体的个体,所以对象也被称为实例。

  • 对象具有数据属性和方法属性

  • Python中对象(实例)本身只有数据属性,但是python的class机制会将类的函数绑定到对象上,称为对象的方法,或者叫绑定方法,绑定方法唯一绑定一个对象,同一个类的方法绑定到不同的对象上,属于不同的方法,内存地址都不会一样

  • 在类内部定义的属性属于类本身的,由操作系统只分配一块内存空间,大家公用这一块内存空间

  • 创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性:而类中有两种属性:数据属性和函数属性,其中类的数据属性是共享给所有对象的,而类的函数属性是绑定到所有对象的。

  • 创建一个对象(实例)就会创建一个对象(实例)的名称空间,存放对象(实例)的名字,称为对象(实例)的属性

  • 在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类…最后都找不到就抛出异常。

  • .创建出类会产生名称空间,实例化对象也会产生名称空间。

  • 用户自己定义的一个类,实际上就是定义了一个类型,类型与类是统一的。

  • 用户先是从自己的命名空间找,如果找不大,在从类的命名空间找。

  • 通过类来访问,访问的是函数,通过对象来访问,访问的是方法,在类内部定义的方式实际上是绑定到对象的身上来用的。

  • 类的数据属性是大家共有的,而且大家的内部地址是一样的,用的就是一个类的函数属性是绑定到大家身上的,内部地址不一样,绑定方法指的是绑定到对象身上。
    绑定方法:绑定到谁的身上,就是给谁用的,谁来调用就会自动把自己当做第一个参数传入。
    定义在类内部的变量,是所有对象共有的,id全一样,
    定义在类内部的函数,是绑定到所有对象的,是给对象来用的,obj.fun()会把obj本身当作 一个参数来传递。

  • 在类内部定义的函数虽然可以由类来调用,但是并不是为了给类用的,在类内部定义的函数的目的就是为了绑定到对象身上的。

  • 在类的内部来说,__init__是类的函数属性,但是对于对象来说,就是绑定方法。

  • 命名空间的问题:先从对象的命名空间找,随后在从类的命名空间找,随后在从父类的命名 空间找。

  • 类的相关方法(定义一个类,也会产生自己的名称空间):

方法名功能
类名.__name__类的名字(字符串str)
类名.__doc__类的文档字符串
类名.__dict.__类的字典属性、名称空间
类名.__module.__类定义所在的模块
类名.__base__类的第一个父类
类名.__bases__类所有父类构成的元组
类名.__class__实例对应的类(仅新式类中)

经典类与新式类辨析:
Python 2.x:默认为经典类,继承object为新式类
Python 3.x:默认为新式类,自动继承object,不用写
经典类 继承为深度优先;新式类 继承为广度优先

五、定义类、构造函数(构造方法) __init__

构造函数在构造对象时自动被对象调用,对对象进行初始化。

  • 一个类只能有一个构造函数
  • 构造函数无返回值
  • 对象实例化时,一定会调用构造函数
//基本形式
class  [ClassName]([Inherited class]):
     def __init__(self,*args,**kwargs):
     #使用构造函数初始化类,*args 普通动态参数
     #**kwargs 关键字动态参数

例子:

#基于Python 3.7 IDLE
>>> class Car():
	count = 0 #计数器
	def __init__(self,name):
	#self指针变量只想当前时刻正在处理的对象
		self.name=name
		#self.name=name的含义为将局部变量name的值发给当前时刻正在创建的对象中                    
		#的name成员
		Car.count += 1

>>> if __name__ =='__main__':
	bmw=Car('BMW') #实例化对象
	print(bmw.__dict__)
	audi=Car('Audi')
	print(audi.__dict__)
	print(count)

{'name': 'BMW'}
{'name': 'Audi'}
2

六、类的继承、方法重载

类的继承是指从已有的类那里获取其已有的属性和方法。
特点:

  • 减少代码和灵活订制新类
  • 一个子类可以有多个父类
  • 子类具有父类的属性和方法(非私有成员)
  • 子类不能继承父类的私有属性/方法
  • 子类可以修改父类的方法(方法重载)
  • 子类可以添加新的方法

继承的语法:

class ClassName: ([FatherClassName1,FatherClassName2])

重载的语法:
直接定义与父类同名的方法

修改父类的方法:
在重载的方法中调用父类方法(super关键字)
添加相应的业务逻辑
注意:多重继承时如何修改父类方法
例子:

>>> class Father():
	def print(self):
		print('I am Father')
		
>>> class Son(Father):
	def print(self):
		super().print() #调用父类方法
		print("I am Son")

>>> if __name__=='__main__':
	fa=Father()
	fa.print()
	print("----")
	son=Son()
	son.print()

	
I am Father
----
I am Father
I am Son

继承原理:MRO列表
对于每个类,python都会计算出一个方法解析顺序(MRO)序列–所有基类的线性顺序列表

#承接下方代码
print(SonClass.mro())
#结果
[<class '__main__.SonClass'>, <class '__main__.AbstractClass'>, <class 'object'>]

python通过在MRO列表中遍历,直到找到第一个匹配属性的类。
遍历原则:

  • 子类先于父类
  • 多个父类则由MRO列表顺序决定
  • 存在多个选择,选择第一个

七、抽象类与接口

抽象类与接口都是类概念的扩展。
抽象类是一个专门创建来做父类的类,类似于一个模板类,目的是根据他的格式来创建和修改新的类。

  • 抽象类不能实例化对象
  • 通过子类继承抽象类,再由子类创建对象
  • Python中抽象方法定义的方式:利用abc模块实现抽象类,在Java当中如果一个方法没有执行体就叫做抽象方法,而在Python中不是以执行体的有无作为标准,而是以一个方法是否有@abc.abstractmethod装饰器作为标准,有则是抽象方法
  • 抽象方法不存在所谓重写的问题,却存在着实现的问题
  • 含有抽象方法的类一定是抽象类,但是抽象类不一定含有抽象方法,此时也就没有什么意义了
  • 抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计

例子

import abc
class AbstractClass(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    #装饰器声明抽象方法
    def absmethod(self):
        pass

    def commonmethod(self):
    #抽象类中的普通方法
        print("This is commonmethod")


class SonClass(AbstractClass):
    def absmethod(self):
        print("rewrite absmethod")

if __name__=='__main__':
    sonclass=SonClass()
    sonclass.absmethod()
    print('----')
    sonclass.commonmethod()
#结果
rewrite absmethod
----
This is commonmethod

接口:
在Java中,接口(Interface)的结构与抽象类类似,本身也具有数据成员和抽象方法,但也有不同:

  • 数据成员都是静态的且必须初始化;
  • 方法成员必为抽象方法,无一般方法;

但在Python中并无Interface的概念,但也可以用其他方法实现接口

  • 抽象类与抽象方法实现接口
  • 普通类和无功能的一般方法,通过继承和重载实现接口

八、组合

组合的定义:一个类的某个属性成员是另一个类
例子:

class ClassA():
    def __init__(self,name,num):
        self.name = name
        self.num = num

    def statement(self):
        print('This is %s ,We hava %s students'%(self.name,self.num))

class Sum():
    def __init__(self,name,num):
        self.name=name
        self.info=ClassA(name,num)


if __name__== '__main__':
    summary=Sum('classa',50)
    summary.info.statement()
#结果
This is classa ,We hava 50 students

九、鸭子类型与多态

多态:一种类型具有多种类型的能力,

  • 允许不同的对象对同一消息做出灵活的反应,
  • 以一种通用的方式对待可使用的对象
  • 非动态语言必须通过继承和接口实现

Python中的多态:通过继承实现(子类作为父类使用),子类重载父类的方法

动态语言与鸭子类型:
鸭子类型:当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
动态语言:

  • 变量绑定的类型具有不确定性
  • 函数和方法可以接受任何类型的参数
  • 调用方法时不检查提供的参数类型
  • 调用时是否成功是由参数的方法和属性确定的,不成功则抛出错误
  • python中不定义接口

多态的优点:

  • 实现开放的扩展和修改的封闭
  • 更据灵活性

例子:

class Timer(object):
    def run(self):
        print("Start...")

def run_twice(a):
    a.run()
    a.run()
timer = Timer()
run_twice(timer)
#结果
Start...
Start...

十、封装

面对对象编程中,为保证程序的安全,不能让外部类直接访问类内部的属性和方法,外部类要通过类内部定义的一些按钮才能访问类内部的成员
Python实现封装:
双下划线(访问控制符)实现隐藏属性,在内部可以直接访问,在外部则需要通过定义的按钮才能访问
例子:

class Car():
    def __init__(self,name,type):
    #构造函数被封装
        self.__name=name
        self.type=type

    car='This is car'  #没被封装,类外可以调用
    __bus='This is bus'

    def reset(self,name,type):
        if not isinstance(name,str):
            raise TypeError("Name must be string ")
        if not isinstance(type,str):
            raise  TypeError("Type must be string")
        self.__name=name #在类内可以调用
        self.type=type

    def print(self):
        print('This is %s %s'%(self.__name,self.type))

if __name__== '__main__':
    car=Car('Audi','A6')
    car.print()
    print(car.car)
    car.reset('BMW','X1')
    car.print()
    print(car.__bus)
#结果
This is Audi A6
This is car
This is BMW X1
   print(car.__bus)       #(Line 26)
AttributeError: 'Car' object has no attribute '__bus'

十一、三大装饰器@property、@classmethod、@staticmethod

一些参考的csdn博文:
https://blog.csdn.net/a2011480169/article/details/73087097
https://blog.csdn.net/flyDeDog/article/details/68925795
https://blog.csdn.net/Gscsd_T/article/details/78938116

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值