Python3学习笔记之-面向对象编程(第二篇)

Python3学习笔记之-面向对象编程(第二篇)


目录

Python3学习笔记之-面向对象编程(第二篇)

一、继承和多态

 1.继承

2.多态

 3.静态语言 vs 动态语言

二、获取对象信息

1.使用type()

2.使用isinstance()

3.使用dir()

三、实例属性和类属性


一、继承和多态

在面向对象程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类,被继承的class称为父类。

继承是面向对象的第二个特性、多态是面向对象的第三个特性由于继承和多态这两个特性关系比较暧昧,所以必须要放在一起讲解。

为了更加通俗易懂的讲解继承和多态,我也找了许多资料,接下来是我总结下来讲的非常好的一个讲解方法:

继承与多态理解之--------《女娲造人》

很久以前,世界上啥也没有,于是女娲就想捏个人吧,于是心中浮现一个物种的初始状态:

 但是觉得这个设计太粗糙,于是开始完善细节:

这个动物设计方案,增加了眼睛,嘴巴,还增加了 可以吃东西,可以跑,可以叫的能力。

但是这个设计方案,还是不够细致,不够具体,于是继续添加细节:

这时候女娲觉得如果世上只有人类的话,还是太孤独了,

于是,女娲, 根据 动物设计方案, 设计了 鸭类设计方案, 狗类设计方案, 猫类设计方案

 

 1.继承

现在已经有四种详细的设计方案了,他们似乎都有几个共同的特点----这4种设计方案中的东西,都有四肢,都有2个眼睛,1个鼻子,1个嘴巴,他们还可以吃东西,可以跑,可以叫

而且,这4个设计方案的某些特征,也存在于 在上一张 动物设计方案 中,这就是 面向对象的第二个特征,继承

 那么什么才是继承呢?

这4个设计方案,是基于 动物设计方案,完善优化而来, 动物设计方案的几个特性,我们沿用到了人类设计方案、鸭类设计方案、狗类设计方案、猫类设计方案中。

我们可以认为,这4个设计方案,是动物设计方案的子女;也可以认为,动物设计方案,是这4个设计方案的父亲, 这4个设计方案,继承了它们父亲的一些特征,比如可以吃东西,可以跑,可以叫。

同样,这张动物设计方案,也继承了最开始的那张物种设计方案,他和物种设计方案,也是父子关系,换成生活中的例子,你继承了你父亲的某些特征,你父亲继承了你爷爷的某些特征。这就是继承。

2.多态

接下来说说,面向对象的另外一个特性, 多态:

这4个设计方案,他们彼此之间,有很多相同的特性,比如,都有身高体重,都有2个眼睛,1个鼻子,都可以叫,都可以走,都可以跑

但是你发现了吗?

就拿 这个特点来说,

  • 鸭子是怎么叫的, 嘎嘎嘎
  • 狗是怎么叫的, 汪汪汪
  • 猫是怎么叫的 喵喵喵
  • 人是怎么叫的呢 啊啊啊~~~~

发现了吗?虽然他们都从父设计图那 继承了 这个特性, 但是他们各自的叫法是不同的

这就叫 多态, 多态就是 从父亲那里继承来的 同一个的行为, 孩子们各自的表现状态不一样

附上一些代码理解:

class Animal(object):   #编写Animal类
    def run(self):
        print("Animal is running...")

class Dog(Animal):  #Dog类继承Amimal类,没有run方法
    pass
#当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时
#候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。
class Cat(Animal):  #Cat类继承Animal类,有自己的run方法
    def run(self):
        print('Cat is running...')
    pass

class Car(object):  #Car类不继承,有自己的run方法
    def run(self):
        print('Car is running...')

class Stone(object):  #Stone类不继承,也没有run方法
    pass

def run_twice(animal):
    animal.run()
    animal.run()

run_twice(Animal())
run_twice(Dog())
run_twice(Cat())
run_twice(Car())
run_twice(Stone())

#结果如下:
Animal is running...
Animal is running...
Animal is running...
Animal is running...
Cat is running...
Cat is running...
Car is running...
Car is running...

AttributeError: 'Stone' object has no attribute 'run'

 3.静态语言 vs 动态语言

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:

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

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

Python的“file-like object“就是一种鸭子类型。对真正的文件对象,它有一个read()方法,返回其内容。但是,许多对象,只要有read()方法,都被视为“file-like object“。许多函数接收的参数就是“file-like object“,你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。

二、获取对象信息

当我们拿到一个对象的引用时,如何知道这个对象是什么类型,有哪些方法呢?

1.使用type()

首先我们来判断对象类型,使用type()函数,基本类型都可以用type()判断:

type(123)
#<class 'int'>
type('str')
#<class 'str'>
type(None)
#<type(None) 'NoneType'>

type()函数返回的是什么类型呢?它返回对应的Class类型。如果我们要在if语句中判断,就需要比较两个变量的type类型是否相同:

type(123)==type(456)
True
type(123)==int
True
type('abc')==type('123')
True
type('abc')==str
True
type('abc')==type(123)
False

 判断基本数据类型可以直接写intstr等,但如果要判断一个对象是否是函数怎么办?可以使用types模块中定义的常量:

import types
def fn():
    pass

type(fn)==types.FunctionType
True

type(lambda x: x)==types.LambdaType
True

type((x for x in range(10)))==types.GeneratorType
True

2.使用isinstance()

对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数。

  • type() 不会认为子类是一种父类类型,不考虑继承关系。

  • isinstance() 会认为子类是一种父类类型,考虑继承关系。

语法:

isinstance(object, classinfo)

参数:

  • object -- 实例对象。

  • classinfo -- 可以是直接或间接类名、基本类型或者由它们组成的元组。

返回值:

如果对象的类型与参数二的类型(classinfo)相同则返回 True,否则返回 False。

class A:
    pass
 
class B(A):
    pass
 
isinstance(A(), A)    # returns True
type(A()) == A        # returns True
isinstance(B(), A)    # returns True
type(B()) == A        # returns False

3.使用dir()

如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,举例:

#获得一个str对象的所有属性和方法
dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']

类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:

len('ABC')
3
'ABC'.__len__()
3

除了用途特殊的方法,其他的都是普通属性和方法,但是仅仅把属性和方法列出来还是不够,我们可以配合getattr()setattr()以及hasattr()直接操作一个对象的状态:

class MyObject(object):
    def __init__(self):
        self.x = 9
    def power(self):
        return self.x * self.x

obj = MyObject()

紧接着,可以测试该对象的属性:

hasattr(obj, 'x') # 有属性'x'吗?
True
obj.x
9
hasattr(obj, 'y') # 有属性'y'吗?
False
setattr(obj, 'y', 19) # 设置一个属性'y'
hasattr(obj, 'y') # 有属性'y'吗?
True
getattr(obj, 'y') # 获取属性'y'
19
obj.y # 获取属性'y'
19

也可以获得对象的方法:

hasattr(obj, 'power') # 有属性'power'吗?
True
getattr(obj, 'power') # 获取属性'power'
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
fn # fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
fn() # 调用fn()与调用obj.power()是一样的
81

三、实例属性和类属性

由于python是动态语言,根据雷创建的实例可以任意绑定属性;

给实例绑定属性的方法是通过实例变量,或者通过self变量:

class Student(object):
    def __init__(self, name):
        self.name = name

s = Student('Bob')
s.score = 90

但是如果student类本身需要绑定一个属性呢?可以直接在class中定义属性,这种属性是类属性,贵student所有:

class Student(object):
    name = 'Student'

当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到。来测试一下:

class Student(object):
    name = 'Student'

s = Student() # 创建实例s
print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
Student
print(Student.name) # 打印类的name属性
Student
s.name = 'Michael' # 给实例绑定name属性
print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
Michael
print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
Student
del s.name # 如果删除实例的name属性
print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
Student

从上面的例子可以看出,在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。

实例属性属于各个实例所有,互不干扰;

类属性属于类所有,所有实例共享一个属性;

不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是小先生

知识是无价的,白嫖也可以的。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值