• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

类与对象

互联网 diligentman 2周前 (04-29) 8次浏览

面对对象编程

  • 一、类与对象
    • 1.类
    • 2.对象
  • 二、类的使用
    • 1.类的共性和个性
    • 2.类的创建
    • 3.类的调用
      • (1)直接用定义的类名调用
      • (2)将类实例化,通过实例调用
  • 三、类的创建要素
    • 1. 解密self
    • 2.类的初始化
  • 四、类的继承与定制
    • 1.什么是继承
    • 2.我们为什么使用继承
      • 多层继承
      • 多重继承
    • 3.什么是定制
      • 利用定制新增代码
      • 利用定制修改代码

一、类与对象

1.类

类是多个类似事物组成的群体的统称。类的概念能够帮助我们快速理解和判断事物的归属。我们熟知的几种数据类型也都属于类。

print(type('word'))
print(type(6666))
print(type(66.66))
print(type(['a','b','c']))
#输出为: <class 'str'>
#         <class 'int'>
#         <class 'float'>
#         <class 'list'>

从以上的例子中不难发现,'word’为字符串(str)类中的一个实例,其他同理。在python中,每个类都有很多实例,这些实例都具有对应类的共性,并且我们还可以根据自己的需要创建系统里没有的的类。

2.对象

对象这个概念在编程之中的应用非常普遍,类是对象,实例也是对象,所有的事物都可以称之为对象。

二、类的使用

1.类的共性和个性

同一个类中的不同实例之间存在着共同点,这些共同点又同时是这个类区别于其它类的依据。细分之下会发现,这些相同点有两个特点,其一是属性(what),描述类的性质,另一种是方法(how),用于描述类的用途。

2.类的创建

了解了类的共性和个性,我们就可以进行类的创建了。创建类首先需要定义类的属性,之后需要定义类的方法。其格式为:

class 类名:
	定义属性,格式为“属性名=属性值”
	def 方法名(self):
		方法内容

class是关键词,类名首字母需要大写。类名可以自定义,但关键词不能变。
注:类名中间不能出现空格。

例如,创建一个类,属性为男演员,工作为拍戏:

class Actor :
    occ='男演员'
    def life(self):
        print('我们的工作是拍戏')
print(type(Actor))
#运行结果为:<class 'type'>

一个类可以有很多属性,也可以有很多方法,创建时可以根据自己的需要进行调整,例如创建一个类,介绍德云社的部分成员和德云社经营业务:

class Deyun:
    act1='郭德纲'
    act2='于谦'
    act3='岳云鹏'
    act4='孙越'
    def job1(self):
        print('说相声')
    def job2(self):
        print('演电视剧')
    def job3(self):
        print('做服装')
    def job4(self):
        print('做餐饮')
print(type(Deyun))
#运行结果为:<class 'type'>

属性和方法可以设置很多,自然也可以不予设置。如果遇到不需要定义属性或方法的情况,均可直接跳过对应步骤。如:

class Actor:
    def life(self):
        print('我们的工作是拍戏')
  #没有定义属性
class Deyun():
    act1='郭德纲'
    act2='于谦'
    act3='岳云鹏'
    act4='孙越'
  #没有定义方法
class Name:
    pass
  #属性和方法都无定义
print(type(Actor))
print(type(Deyun))
print(type(xiao))
#运行结果为:<class 'type'>
            <class 'type'>
            <class 'type'>

3.类的调用

之前讲到了类的创建过程,接下来继续讲述类的调用。调用类主要指的是调用类的属性和方法。想要调用类有两种方式:

(1)直接用定义的类名调用

其格式为“类名().属性名"和“”类名().方法名()”:

class Actor:
    occ='男演员'
    def life(self):
        print('我们的工作是拍戏')
Actor().occ#典型错误用法,无输出
print(Actor().occ)
Actor().life#典型错误用法,无输出
Actor().life()
#输出结果为:男演员
           #我们的工作是拍戏

下面我们对上例结果进行分析:
通过单步运行发现:
代码“print(Actor().occ)”的运行结果为“男演员”,这句代码可以输出属性名;
代码“Actor().life()”的运行结果为“我们的工作是拍戏”,这句代码可以调用方法“life”。

代码“Actor().occ”与“Actor().life”并没有输出结果,导致这一现象出现的原因为:
Actor().occ”是一个字符串,不借助打印函数无法将其结果直接输出;
life”本质上为类内部定义的函数,“Actor().life”这种表述计算机不会解读为函数调用,因此不会输出我们想要的结果。
注:这两种错误的书写格式都不会引起计算机报错,因此写代码时要格外小心
实际上,“类名()”这种表示也可以理解为类的实例化,只是这样的方式并没有给实例命名,当需要用到多个实例的时候,用这个方法无法保存内容,因此实际应用中很少使用这种方式。

(2)将类实例化,通过实例调用

创建一个实例,让其拥有类的属性和方法而后对实例进行操作:

class Actor:
    occ='男演员'
    def life(self):
        print('我们的工作是拍戏')
        
cheng=Actor() #注意:由于life方法本质是函数,所以必须要有传参
              #如果此处Actor后不加(),则无法调用life方法
print(cheng.occ)
cheng.life()
#输出结果为:男演员
           #我们的工作是拍戏

一个类可以创建很多实例,这些实例有着相同的属性和方法。
以上两种方式的实际用法以及需要注意的点一样,在此不做赘述。

拓展:计算机如何表述实例的类型以及如何输出实例的地址
运行以下代码:

class Actor ():#之前强调过类的实例化必须加(),
               #所以我在此也加上(),降低bug检查难度
               #之后的例子里也会延续这个习惯
    pass
cheng=Actor()
print(type(cheng))
print(cheng) #计算机会输出实例的地址
#输出结果为:<class '__main__.Actor'>
           #<__main__.Actor object at 0x000001745D0C7240>

cheng”这个实例是名为“演员类”的类型,请记住计算机的输出方式。

三、类的创建要素

创建类有两大要素,分别为self参数类的初始化

1. 解密self

之前我们介绍过,方法本质上相当于函数,而self在定义类的方法时几乎称得上是“套路”般的存在,但是目前为止的代码中都没有用到这个参数的地方,就连调用方法需要传递参数时都不可以把它当做普通参数一样赋值:

  #我们希望计算机输出:男演员的工作是拍戏
class Actor ():
    occ='男演员'
    def life(self):
        print(self+'的工作是拍戏')
entertain=Actor()
entertain.life(entertain.occ) #如果self是普通形参,那么其可以接收
                              #'entertain.occ'所代表的字符串'男演员'

#运行时计算机报错

类与对象

那么self放在这里到底有什么用呢?其实它的实际作用为在类的实例化中接收传入的数据。可是这句话究竟是什么意思呢?
先让我们观察以下代码:

class Actor (): 
    occ='男演员'
    def life(self,name):
        print(name+'的工作是拍戏')
entertain=Actor()
entertain.life(entertain.occ)
#运行结果为:男演员的工作是拍戏

这段代码跑出来是我们想要的结果,但实际上这样的代码实际上已经写累赘了。回到我们之前遇到的错误,错误提示为:“Life()接受1个位置参数,但给出了2个”。这段提示告诉我们,在代码运行的时,self会自然而然接收到一个参数,那么这个参数会是什么呢?

class Actor ():
    occ='男演员'
    def life(self):
        print(self) #让方法输出self参数的内容
entertain=Actor()
print(entertain)
entertain.life()
#运行结果为:<__main__.Actor object at 0x0000024FED0870F0>
           #<__main__.Actor object at 0x0000024FED0870F0>

看到这里是不是恍然大悟了?原来self函数存放的内容是entertain实例的地址!
我们可以通过这个地址来找到属性,也就是说,完成输出“男演员的工作是拍戏”这句话的代码,可以写成以下的模样:

class Actor ():
    occ='男演员'
    def life(self):
        print(self.occ+'的工作是拍戏')
entertain=Actor()
entertain.life()

同样地,self也可以调用所在类中的其他方法:

class Actor ():
    def occ (self):
        print('男演员',end='')
    def life(self):
        self.occ()
        print('工作是拍戏')
Actor().life()
#运行结果为:男演员工作是拍戏

总结起来,self的独特之处在于其先在类方法中占一个位置,实例创建需要调用。并且self可以找到所在类的地址,方便之后调用类的属性和其他方法。

2.类的初始化

每个类中都存在一个初始化方法,这个方法不需要调用,在通过类创建实例对象的时候初始化方法会自动执行。因此在编程之中,我们可以用这种方式让类自动调用方法。
初始化的定义:

def __init__(self):  # init两边是双下划线
    方法内容

下面我们举个例子说明一下:

class Actor ():
    occ='男演员'
    def __init__(self):
        print(self.occ+'的工作是拍戏')
Actor()
#输出结果为:男演员的工作是拍戏

从这个例子中我们可以看到,我们没有调用函数,但这个方法在类的调用过程中会自动执行。利用init初始化方法的特点,我们可以在初始化方法中完成类属性的创建及类属性的赋初值。比如:

class Actor1 ():
    occ=0
    def __init__(self):
        self.occ='男演员'
act1=Actor1()
print(act1.occ)
############################
class Actor2 ():
    occ=0
    def defi(self):
        self.occ='男演员'
act2=Actor2()
print(act2.occ)
#输出结果为:男演员
            #0

同时,,我们就掌握了一种自定义属性赋值的方法:

class Actor ():
    occ='男演员'
    def __init__(self,name,age,work): #为Actor类创建三个新属性
        self.name=name
        self.age=age
        self.work=work
        #调用这些创建的新属性时,属性名前不要加“self.”
cheng=Actor('成龙',67,'拍电影') 
print('%s是%s,他今年%d岁,曾经的工作是%s'%(cheng.name,cheng.occ,cheng.age,cheng.work))
#输出结果为:成龙是男演员,他今年67岁,曾经的工作是拍电影

这个例子不仅给我们演示了自定义属性的方法,也进一步展示了self的另一个神奇之处:它不仅可以通过寻址找到类的属性,也可以用于规划类的空间,辅助创建新属性。
通过上述举例,我们发现了很多类初始化的妙用:可以在初始化方法中完成类的属性赋初值或创建属性,甚至可以在方法中创建自定义的属性。

四、类的继承与定制

我们之前讲述了类的几种基本用法,下面,我们要开始拓展一下类的用法,继承与定制:

1.什么是继承

类的继承和我们日常生活中说的继承十分相似,指的是一个类从另外一个或多个类中复制了一部分或者所有属性和方法。其语法为:

class X (Y): # 这里X是新的类,Y是原有的类,
#              执行这句代码后,X将拥有Y的所有属性和方法
    定义X需要的其他属性或方法,不需要则用pass语句

在这段定义中,X和Y有自己的称呼:
X:Y的子类,表示X将继承父类(又名超类)Y的属性与方法
Y:X的父类(超类)
所有的类都有一个共同的父类,即object,我们称其为根类。isinstance() 函数可以帮我们判断类的父子关系,下面我们举个例子:

class Actor():
    pass
class Deyun(Actor):
    pass
print('判断Deyun是否为Actor的子类:')
print(isinstance(Deyun(),Actor))
print('判断Actor是否为Deyun的子类:')
print(isinstance(Actor(),Deyun))
print('判断Actor是否为Object的子类:')
print(isinstance(Actor(),object))
print('判断Deyun是否为Object的子类:')
print(isinstance(Deyun(),object))
#输出结果为:判断Deyun是否为Actor的子类:
           #True
           #判断Actor是否为Deyun的子类:
           #False
           #判断Actor是否为Object的子类:
           #True
           #判断Deyun是否为Object的子类:
           #True

通过上例大家了解到,这个函数的使用格式为isinstance(_,Y),其中,“_”处填写X类的一个实例,isinstance()可以借助这个实例判断X类是否为Y类的子类。
下面我们通过一个例子进一步了解类的继承:

class Actor ():
    occ='男演员'
    def life(self):
        print('我们的工作是拍戏')
class Director_team (Actor):
    occp='女演员'
    def work(self):
        print('我们还要负责编剧')
    def number(self):
        print('我们团队有%s和%s'%(self.occ,self.occp))
wang=Director_team()
wang.life()
wang.work()
wang.number()
#输出结果为:我们的工作是拍戏
           #我们还要负责编剧
           #我们团队有男演员和女演员

在这个例子里,Director_team不但继承了父类所有的属性和方法,还拥有了父类没有的属性和方法。
还记得我们之前讲过的类的初始化吗?观察下面的例子:

class Lead ():
    lea='男导演'
    def __init__(self,name,age):
        self.name=name
        self.age=age
        print('____________________________')

class Actor_team(Lead):  #此处Lead类并未进行类的初始化,因此也不能传参
    occ='男一号'
team=Actor_team("钱雁秋",52)  #执行到此处才完成Lead类的初始化
                             #因此也是执行到此处才需要传参
print('这次的带队人是%s,他是%s,
%d岁'%(team.name,team.lea,team.age))
#运行结果:____________________________
        #这次的带队人是钱雁秋,他是男导演,52岁

通过单步运行以及观察结果发现,Lead类的初始化在实例化时完成(即计算机执行“team=Actor_team(“钱雁秋”,52)”这个语句时),而在继承的过程中(即执行“class Actor_team(Lead):”语句时),Lead类没有执行类的初始化。

2.我们为什么使用继承

观察类的定义不难发现,子类和父亲类、爷爷类的属性和方法有大量重复,使用继承不但可以让代码看上去简洁,还能清楚地表述出类和类之间的关系,增强代码的可读性。同时,一个子类可以继承多个父类,这便有了我们以下的讨论:多层继承和多重继承

多层继承

多层继承指子类不仅继承了他的父类,还继承了它的父类的父类(我们暂时称之为爷类),甚至可以继承自爷爷类的父类…
举个例子:

class Show:
    occupation='制片'
# 演员类
class Actor (Show):
    job='拍戏'
# 动作片演员类
class Action_actor(Actor):
    work='打戏'
Mr_cheng=Action_actor()
print(Mr_cheng.occupation)
print(Mr_cheng.job)
print(Mr_cheng.work)
# 运行结果为:制片
            #拍戏
            #打戏

上述例子展示了子类继承了父类、爷爷类的所有属性,实际上方法也是可以这样继承的:

class Show:
    def occupation(self):
        print('制片')
class Actor (Show):
    def job(self):
        print('拍戏')
class Action_actor(Actor):
    def work(self):
        print('打戏')
Mr_cheng=Action_actor()
Mr_cheng.work()
Mr_cheng.job()
Mr_cheng.occupation()
# 运行结果为:制片
            #拍戏
            #打戏

多层继承是类在纵向深度上的拓展,子类创建的实例,可以调用所有父类的属性和方法。

多重继承

多重继承:一个子类同时继承了许多父类,其语法为:

class Z(X,Y):
    定义新的内容和方法

Z可以继承X和Y的所有属性和方法,如果X和Y有相同名称的方法时,Z会优先继承X类中相应名称方法。我们称这种现象为Z更相似于左边的X类:

# 音乐人
class Musician():
    loveMusic = True
    speak="唱"
    def intr(self):
        print("我最喜欢音乐")
    def work(self):
        print('我会谱曲')
#演说家
class Orator():
    speak = "快速说"
    def intr(self):
        print("我最欢演说")
    def job(self):
        print('我会作词')
# Rapper继承了音乐人与演说家
class Rapper(Musician,Orator):
    pass
pan=Rapper()
print(pan.loveMusic)
print(pan.speak)
pan.intr()
pan.work()
pan.job()
#输出结果为:True
           #唱
           #我最喜欢音乐
           #我会谱曲
           #我会作词

多重继承可以同时继承三个或更多的父类,并且括号内越靠左边的父类越和子类相似。同时继承三个父类的语法为:“class Q(X,Y,Z):”。多重继承是类在横向上的深度拓展,遇到父类有相同的种类和方法时,子类只会继承更“亲”的父类。

3.什么是定制

子类继承了父类的属性和方法,但如果这些属性和方法并不足以描述子类的特征的话该怎么办呢?Python中给我们准备好了解决办法,这便是类的定制。
简单地说,类的定制就是子类可以在继承父类的基础上,创建新属性、新方法,或修改继承到的属性或方法。

利用定制新增代码

利用定制新增代码用于子类需要用父类没有的属性或方法描述的情况,下面我们举个栗子:

class Musician():
    style = "摇滚"
    def intr(self):
        print("我喜欢唱歌")
    def job(self):
        print(self.name + '要创作词')
class Sing(): # 这里是标记1,后文会讲到这里
    style = "古风"
    def __init__(self,name):
        self.name=name
class Old_self_singer (Sing,Musician):
    lovemusic=True
    # 这是新增的属性,Sing、Musician类中都没有这种属性
    def like(self):
        print('我艺名%s,我最喜欢唱%s歌曲'%(self.name,self.style))
        # 这是新增的方法,Sing、Musician类中都没有这种方法
he=Old_self_singer('河图') #这里是标记2
he.like()
print(he.lovemusic)
#输出结果为:我艺名河图,我最喜欢唱古风歌曲
           #True

我们举的例子是多重继承,多层继承中同样适用。
这个like()方法是两个父类中都没有的方法,但是我们的子类Old_self_singer中新添了这种方法。细心的小伙伴应该已经往回翻看了,我们之前在解析类的继承的例子中,就已经用到了利用定制新增代码的方法了。
此外,这个例子又一次告诉了大家Old_self_singer类和哪个父类更亲~因为河图大大说他最喜欢唱古风歌曲,所以也就说明Old_self_singer类优先继承了Sing类。
大家回到标记1,这里应用了类的初始化。所以标记2处,我们需要给出一个实参。需要注意一下,如果有多个需要传递的参数,传参顺序不要弄乱。

利用定制修改代码

利用定制修改代码用于父类的某些属性或方法并不适用于子类的情况。这种操作其实和新增代码有些类似,我们用下例说明:

class Musician():
    style = "摇滚"
    def intr(self):
        print("我喜作曲")
    def job(self):
        print(self.name + '要创作词')
class Sing(Musician):
    style = "古风"
    def intr(self):
        print("我喜唱歌")
he=Sing()
print(he.style)
he.intr()
#输出结果为:古风
           #我喜唱歌

是不是很简单?到这里我们关于类的讲述就先告一段落了,感谢小伙伴们看到这里,创作不易,希望大家多多点赞关注支持一下~


程序员灯塔
转载请注明原文链接:类与对象
喜欢 (0)