Python中类,对象,面向对象编程(OOP)的理解

目录

1,给大家介绍对象

2,对象的创建与使用

2.1,对象的创建

2.2,对象的使用

2.3,添加和删除属性

2.4,构造方法(__init__)和析构方法(__del__)

2.5,对象的私有属性

3,面向对象的精髓(封装,继承,多态)

3.1,封装

3.2,继承

3.2.1,子类继承父类的非私有属性和方法

3.2.2,父类的私有属性和方法

3.2.3,子类调用父类的构造方法

3.2.4,多继承

3.3,多态


Python是一门面向对象编程的语言,第一次接触OOP(Object Oriented Program)会感到有所不适应,但慢慢理解才会发现OOP的妙处。

1,给大家介绍对象

对象=属性+方法。Python中任何东西都可以称为对象,如一个列表变量(list1)就是一个对象,可以使用列表的方法来处理该对象,比如 list1.append(‘5’),list1.sort()。

方法和函数的区别:在类中定义方法的形式和函数差不多,但不称为函数,而称为方法。方法的调用需要绑定到特定对象上,而函数不需要。

说到对象,就会谈到类(class),其实类也是一个对象:类对象。不过为了区分,后面将类对象称为类,而由类实例化的对象才称为对象。一个简单的Python的类的定义如下,定义了一个名为 Dog  的类,拥有属性legs 和方法 run()。

class Dog(object):
    legs = 4                  #定义属性
    def run(self, name):      #定义方法
        self.name = name
        self.color = 'yellow'
        print("I'm %s, I'm running..." %self.name)

在Python3中,object类是所有类的父类。若在定义类时没有标注,则默认该类是继承自object的,即 class Dog: 与 class Dog(): 与 class Dog(object): 是等价的 。

2,对象的创建与使用

2.1,对象的创建

有了类之后,就可以创建基于类的对象

dog1 = Dog()
dog2 = Dog()

这样就创建了一个名为 dog1 和一个名为 dog2 的 Dog 类型的变量。类相当于一个图纸,而dog1,dog2 就相当于依照图纸建起来的两栋房子,在类中的方法定义的时候,会传入self参数,这个参数就相当于房子的建造地址,可以通过self找到不同的房子。

2.2,对象的使用

dog1 和 dog2 对象是基于Dog类的,那么就拥有了 Dog类中所有的属性和方法,通过点操作可以直接调用类属性和方法。对象可以访问和修改类属性,上文说了类其实也是个对象,那么通过类也可以去访问类属性和方法

>>> class Dog:
	legs = 4
	def run(self, name):
		self.name = name
		self.color = 'yellow'
		print("I'm %s, I'm running..." %self.name)

>>> dog1 = Dog()     #实例化一个名为 dog1 的 Dog 类
>>> dog2 = Dog()
>>> dog1.legs        #通过对象访问类属性的值
4
>>> dog2.legs
4
>>> Dog.legs         #通过类访问类属性的值
4
>>> Dog.legs = 2     #通过类修改类属性的值,所有基于此类的对象中的该属性,
                     #若有过赋值操作,则保持不变,若没有,则跟着改变
>>> dog1.legs
2
>>> dog2.legs
2
>>> Dog.legs
2
>>> dog1.legs = 1    #dog1对象中的legs属性已经修改
>>> Dog.legs = 3     #通过类再次修改类属性值
>>> dog1.legs        #之前已经赋值过了,所以保持不变,为1,而不是3
1
>>> dog2.legs        #没有赋值过,跟着变为3
3
>>> Dog.legs
3

上式中只访问和修改了属性legs,在方法中还有属性(color),这个属性只有在调用了该方法后才能使用,否则会引起错误。

>>> class Dog:
	legs = 4
	def run(self, name):
		self.name = name
		self.color = 'yellow'
		print("I'm %s, I'm running..." %self.name)

>>> dog1 = Dog()
>>> dog1.color
Traceback (most recent call last):                         ## ERROR
  File "<pyshell#102>", line 1, in <module>                ##
    dog1.color                                             ##
AttributeError: 'Dog' object has no attribute 'color'      ## ERROR
>>> dog1.run('大黄')
I'm 大黄, I'm running...
>>> dog1.color               #通过对象调用方法后,能成功访问方法中定义的属性的值
'yellow'
>>> dog1.name
'大黄'
>>> Dog.run(dog1, '旺财')    #相当于dog1.run('旺财')
I'm 旺财, I'm running...
>>> dog1.name
'旺财'

>>> Dog.run(Dog, '哮天犬')   #直接传入Dog类对象也能成功调用类方法
I'm 哮天犬, I'm running...

在执行dog1.run('大黄')的时候,python将其解释为 Dog.run(dog1, '大黄')self参数传入的是类的实例。当一个对象调用类中的方法时,对象会将自身作为第一个参数传给 self 参数,Python 就会知道是哪一个对象在调用方法。

2.3,添加和删除属性

和前文所述的访问更改属性一样,在类中增加或删除属性时,若对象中存在已经赋值的同名属性,则不会跟着改变,若不存在,则对象跟随对应的类改变。也可以通过对象直接单独增添或删除属性。

 上面标明了也可以删除方法内定义的属性

2.4,构造方法(__init__)和析构方法(__del__)

在类的方法中,有两种比较特殊的方法,分别在类创建的时候调用,分别为构造方法和析构方法。实际上,通过实例化创建一个对象后,系统会马上调用__init__()方法。每个类都有个默认的__init__()方法,即创建对象后什么都不做,直接返回对象。如果自定义类时显式定义了__init__()方法,则创建完对象后会调用这个定义的__init__()方法。__del__()方法也类似。

>>> class Dog():
	legs = 4

>>> dog1 = Dog()                 #默认什么也没做  
>>> del dog1                     #默认什么也没做
>>> class Dog():
	legs = 4
	def __init__(self):
		print('对象创建了')
	def __del__(self):
		print('对象被清除了')

>>> dog1 = Dog()                 
对象创建了                        #执行显式定义的
>>> del dog1         
对象被清除了                      #执行显式定义的

2.5,对象的私有属性

在python中,变量名称前加了“__”(两个下划线)的类属性是私有属性,加了“__”的方法是私有方法(注意__init__()类型的方法不是私有方法),对象无法通过访问公共属性和公共方法那样去访问私有属性和私有方法。可以在内中定义方法去让类访问和修改私有属性。

公共属性本来可以直接访问和修改,通过设置私有属性还需要设置两个新方法(set,get)去访问和修改私有属性,那么为什么要这样做呢?通过下面的例子就可以发现,通过set方法可以帮助我们做参数检查,避免传入无效的参数。

>>> class Student():
	def __init__(self, name, score):
		self.__name = name
		self.__score = score
		
	def getAttrs(self):
		print('同学:%-3s  成绩:%-3d' %(self.__name, self.__score))
		
	def setAttrs(self, name, score):
		self.__name = name
		if 0 <= score <= 100:
			self.__score = score
		else:
			print('请输入0到100之间的数')

>>> person1 = Student('小明', 85)
>>> person1.__score                             #对象不能直接访问私有属性
Traceback (most recent call last):
  File "<pyshell#56>", line 1, in <module>
    person1.__score
AttributeError: 'Student' object has no attribute '__score'
>>> person1.getAttrs()                         #对象通过方法去访问私有属性
同学:小明   成绩:85 
>>> person1.setAttrs('小红', 90)                #对象通过方法去设置私有属性
>>> person1.getAttrs()
同学:小红   成绩:90 
>>> person1.setAttrs('狗蛋', 115)               #格式检查查到成绩输入不合格
请输入0到100之间的数
>>> person1.getAttrs()                          #__name属性被更改了,但__score格式不合格,没有被更改
同学:狗蛋   成绩:90 

python的私有属性,私有方法都是伪私有,只是采用了name mangling的技术,将私有变量和方法做了名字改编,通过person1._Student__name,就能访问,但不建议使用。

>>> class Student():
	def __init__(self, name, score):
		self.__name = name
		self.__score = score
		
	def __getAttrs(self):
		print('同学:%-3s  成绩:%-3d' %(self.__name, self.__score))

>>> person1 = Student('小明', 85)
>>> person1._Student__name               #访问私有属性成功
'小明'
>>> person1._Student__score
85
>>> person1.__getAttrs()
Traceback (most recent call last):
  File "<pyshell#67>", line 1, in <module>
    person1.__getAttrs()
AttributeError: 'Student' object has no attribute '__getAttrs'
>>> person1._Student__getAttrs()         #调用私有方法成功
同学:小明   成绩:85 

3,面向对象的精髓(封装,继承,多态)

面向对象开发思想具有三个典型特征:封装,继承和多态。

3.1,封装

把对象的属性和行为封装起来,不需要让外界关心内部的具体实现细节,这就是封装的思想。比如一个求面积的类中有计算矩形面积的方法,执行时,只需要传入矩形的长和宽就可以返回一个面积结果。用户执行时出入长和宽的参数,就会得到矩形的面积,而不用去知道方法内部的实行细节。

3.2,继承

3.2.1,子类继承父类的非私有属性和方法

子类能继承父类(基类,超类)的所有非私有的属性和方法,当子类中定义了和基类中同名的方法,会对此方法进行重写(overriding)。重写后的方法必须与父类的方法名称和参数个数相同

>>> class Animals(object):
	legs = 4
	def run(self):
		print('我能移动...')

>>> class Dog(Animals):
	def sleep(self):
		print('我能睡觉')

>>> class Turtle(Animals):
	def run(self):               #子类对父类方法进行重写
		print('我能爬...')

>>> dog1 = Dog()
>>> turtle = Turtle()
>>> dog1.legs
4
>>> turtle.legs
4
>>> dog1.run()
我能移动...
>>> dog1.sleep()
我能睡觉
>>> turtle.run()       
我能爬...

3.2.2,父类的私有属性和方法

我理解继承中,“子类也会继承父类的私有属性和方法,只不过在子类中也是私有属性和方法,无法直接访问,可以通过改编后的名字访问。”

可以看到,子类直接继承了父类的 __init__() 构造方法 ,无法通过对象直接访问父类的私有属性,通过 “_类名__私有元素” 可以去访问(不建议这样做)。一般情况下,私有属性和方法都是不对外公布的,只能用来做其内部的事情。

建议:子类对象通过调用父类方法的途径来访问父类的私有属性。如 dog1.getAttrs()

3.2.3,子类调用父类的构造方法

当子类中自己定义了构造方法时,那么就相当于对父类的构造方法进行了重写,如果仍然想在子类的构造方法中执行父类的构造方法,可以由两种方法:

1,通过 super() 函数,【推荐使用】

>>> class Animals(object):
	def __init__(self):
		print('定义的父类的构造方法执行了')

>>> class Dog(Animals):
	def __init__(self):
		print('定义的子类的构造方法执行了')

>>> dog1 = Dog()
定义的子类的构造方法执行了

>>> class Dog(Animals):
	def __init__(self):
		super().__init__()                 #执行父类的构造方法
		print('定义的子类的构造方法执行了')

>>> dog1 = Dog()
定义的父类的构造方法执行了
定义的子类的构造方法执行了

2,调用未绑定的父类方法,【不推荐使用】

这样使用需要输入分类的类名Animal,还需要传入参数self,在子类对象例化的时候,self传入是子类对象。而super()函数就不需要。

>>> class Dog(Animals):
	def __init__(self):
		super().__init__()
		print('定义的子类的构造方法执行了')

>>> class Dog(Animals):
	def __init__(self):
		Animals.__init__(self)           #不推荐使用
		print('定义的子类的构造方法执行了')

>>> dog1 = Dog()
定义的父类的构造方法执行了
定义的子类的构造方法执行了

3.2.4,多继承

一个类可以继承自多个父类, class 子类(父类1,父类2,...)

在Python3中,父类中有相同的方法名,但子类没有定义时,在子类调用该方法时:

当子类继承的多个父类间是平行关系的时候,会从左至右搜索。即先搜索父类1,在搜索父类2...

当子类继承的父类关系非常复杂时,Python会使用 mro 算法去找到合适的类。

>>> class House():
	def color(self):
		print('red')

>>> class Car():
	def color(self):
		print('black')

>>> class HouseAndCar(House, Car):
	pass

>>> rv = HouseAndCar()
>>> rv.color()            #调用的是 House 类中的color()方法
red

也可以在子类中指定使用某个父类的属性或方法,而不用管平行父类的先后关系。

>>> class House():
	cost = 'expensive'
	def color(self):
		print('red')

>>> class Car():
	cost = 'chip'
	def color(self):
		print('black')

>>> class HouseAndCar(House, Car):
	cost = Car.cost                #指定使用父类Car中的cost属性
	color = Car.color              #指定使用父类Car中的color()方法         
                                   #注意此处指定父类方法的写法,没有括号
>>> rv = HouseAndCar()
>>> rv.cost
'chip'
>>> rv.color()
black

3.3,多态

多态是面向对象语言最核心的特征。一句话概括就是:不同对象对同一方法响应不同的行动。通过下面这个例子就可以看出来。

>>> class Animal(object):
	def draw(self):
		pass

>>> class Chicken(Animal):
	def draw(self):
		print('小鸡画竹叶...')

>>> class Dog(Animal):
	def draw(self):
		print('小狗画梅花...')

>>> def snow(obj):
	obj.draw()

>>> dog1 = Dog()
>>> snow(dog1)                #同一方法,不同结果
小狗画梅花...
>>> chicken1 = Chicken()
>>> snow(chicken1)            #同一方法,不同结果
小鸡画竹叶...

可以看到,对不同的对象,调用同一方法时,出现了不同的结果。这就是多态的体现。例子中Chicken和Dog都是继承于Animal,即是没有该继承关系,也是多态的体现。snow()函数并没有规定参数的类型,如果传入其他类型的对象也可以,只要该对象就有move()方法,而不是必须要求继承于Animal类。

多态的意义在于,调用方只管调用,不管细节。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值