类和对象

定义和使用类

1.定义类

在python中的基本形式为:

    class <类名>(父类名):

            pass

  • class   定义类的关键字
  • 类名     符合标识符规范的名称
  • 父类名   该类继承的父类名称
  • pass    空语句

其中的父类名称是可选的,如果该类不继承其他类可以连括号都不写;pass语句表示什么也不做,常用来预留语句位置或临时未写等待以后完成,用一个单位缩进表示它属于这个类定义的一部分

注意:class语句末尾要有一个":".

定义一个最简单的类只需要两行代码如下:

class YangQing:
    pass

这个表面看什么都没实现,也没有继承其他的类.但是在Python中,没有继承其他的类,会自动继承系统内建的类object.下面就是在交互环境中看到的MyClass类中从object类继承的属性和方法:

>>> class YangQing:
         pass

>>> dir(YangQing)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__','__ge__', '__getattribute__', '__gt__','__hash__','__init__', '__init_subclass__', '__le__','__lt__','__module__','__ne__','__new__','__reduce__','__reduce_ex__', '__repr__','__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

2.使用类

        类在定义后必须先实例化才能使用,类的实例化跟函数调用类似,只要使用类名加圆括号的形式一般就可以实例化一个类.

         类实例化以后会生成该类的一个实例,一个类可以实例化成多个实例,实例与实例之间并不会相互影响,类实例化以后就可以直接使用了.

实例演示:

#usr/bin/python
#-*-coding:utf-8-*-
class YangQing:                  #定义一个类
    "My name is YangQing."       #该类只有一个说明信息,没有具体语句

myname = YangQing()              #将自定义的类实例化,名称为myname
print("输出类说明:")
print(myname.__doc__)            #输出类实例myname的属性__doc__的值
print("显示类帮助信息")
help(myname)                     #输出类的帮助信息

[代码说明]代码首先定义了一个自定义类YangQing,其类体中只有一行类的说明信息,然后实例化该类,并调用类的属性来显示属性值.

[运行效果]运行结果如图所示:

输出类说明:
My name is YangQing.
显示类帮助信息
Help on YangQing in module __main__ object:

class YangQing(builtins.object)
 |  My name is YangQing.
 |  
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

 从以上类的帮助信息中还可以看到,YangQing类自动继承了builtins.object类,其后显示的属性就是从它继承而来的.

如果在类中定义了函数,也可以用类实例来调用它.

注意: 一般来说,为了区分在类中定义的函数和类外定义的全局函数,将类中定义的函数称为方法.


类的属性和方法

       如前所述实例定义的类YangQing只有一个说明信息,是没有什么使用价值的.要用类来解决实际问题,就要定义一个具有一些属性和方法的类,因为只有这样才符合真实世界中的事物特征.

1.类的方法

       类的方法实际上为类的能力建模的,那么定义一个类的方法让类具有一定的能动性.在类外部用该类的方法就可以完成相应的功能,或改变类的状态,或达到其他的目的.

            类中的方法定义和调用与函数定义和调用的方式基本相同,其区别有

  • 方法的第一个参数必须是self,而且不能省略
  • 方法的调用需要实例化类,并以实例名.方法名(参数列表)形式调用
  • 整体进行一个单位的缩进,表示其属于类体中的内容

此示例演示了一个定义两个方法的类及其使用的方法,代码如下:

class SmplClass:                 #定义一个类SmplClass
    def info(self):              #定义一个类方法 info()
        print('我定义的类!')

    def mycacl(self,x,y):        #定义一个类方法 mycacl()
        return x + y
#
sc = SmplClass()                 #实例化类SmplClass()
print('调用info方法的结果是:')
sc.info()                        #调用类实例sc的info()方法
print('调用mycacl方法的结果是:')
print(sc.mycacl(3,4))

[代码说明]代码首先定义了一个具有两个方法的info()和macacl()的类,然后实例化该类,并调用其两个方法.

[运行效果]如图,第一个方法调用直接输出信息,第二个方法调用计算了参数3和4的和:

调用info方法的结果是:
我定义的类!
调用mycacl方法的结果是:
7

注意:定义方法时,也可以像定义函数一样声明各种各样的参数;方法调用时,不用提供self参数.

在Python语言中的类定义中,可以定义一个特殊的构造方法,即__init__()方法,用于类的实例化是初始化相关数据,如果在这个方法中有相关参数,则实例化是就必须提供.

注意:__init__()方法名中的init前后分别两个下划线.

此示例演示了一个定义类的构造方法的类,代码如下:

class DemoInit:
    def __init__(self,x,y=0):       #定义构造方法,具有两个初始化
        self.x = x
        self.y = y

    def mycacl(self):               #定义应用初始化数据的方法
        return self.x + self.y

dia = DemoInit(3)                   #用一个参数实例化
print('调用mycacl方法的结果1:')
print(dia.mycacl())

dib = DemoInit(3,7)
print('调用mycacl方法的结果2:')
print(dib.mycacl())

[代码说明]代码中定义了构造方法,此构造方法要求提供两个参数x和y,其中y是具有默认值的参数.因此在实例化类时必须提供参数,根据函数的调用规则可知,实例化是至少提供一个参数,也可以提供两个参数.之后,分别以提供一个参数和两个参数的形式实例化类,并调用其mycacl()方法.

[运行效果]分别输出了两个类实例调用方法的结果:

调用mycacl方法的结果1:
3
调用mycacl方法的结果2:
10

类中的方法既可以调用本类中的方法,也可以调用全局函数来完成相关任务.调用全局函数和面向过程中的调用方式完全相同,而调用本类中的方法应用使用以下形式:

        self.方法名(参数列表)

此示例演示了在类中调用类自身的方法和全局函数的实例,其代码如下:

def coord_chng(x,y):                #定义一个全局函数,模拟坐标值变换
    return (abs(x),abs(y))          #将x,y值求绝对值后返回

class Ant:

    def __init__(self,x=0,y=0):     #定义一个构造方法
        self.x = x
        self.y = y
        self.disp_point()           #构造函数中调用类中的方法disp_point()

    def move(self,x,y):             #定义一个方法move()
        x,y = coord_chng(x,y)       #调用全局函数,坐标变换
        self.edit_point(x,y)        #调用类中的方法edit_point()
        self.disp_point()           #调用类中的方法disp_point()

    def edit_point(self,x,y):       #定义一个方法
        self.x += x
        self.y += y

    def disp_point(self):           #定义一个方法
        print('当前位置: (%d,%d)' %(self.x,self.y))

ant_a = Ant()                       #实例化Ant()类
ant_a.move(2,4)                     #调用ant_a实例的方法move()
ant_a.move(-9,6)                    #调用ant_a实例的方法move()

            [代码说明]代码首先定义了一个全局函数coord_chng(),然后定义了一个类,类中定义了一个构造方法,并且在构造方法中也调用了类中的其他方法(disp_point()).此后定义的move()方法同时调用了全局函数coord_chng()和类中的两个方法(edit_point()和disp_point()).

            [运行效果]如图,由于初始化类Ant类时没有给参数,所以使用了默认值0,0;然后调用了move()方法,提供了参数2,4,因此位置变为了(2,4),第三次调用提供了参数-9,6,位置变为了(11,10).

>>>
当前位置: (0,0)
当前位置: (2,4)
当前位置: (11,10)

            一般来说,为了方便程序代码的阅读,理解和维护,函数或方法的行数不要太多.

2.类的属性

            类的属性是对类进行建模必不可少的内容,方法时用来操作数据的,而操作的大部分数据都是类自身的属性,以改变类的状态,在Python中类的属性定义方式和使用方式是很方便的.

            参阅上面定义的Ant类中,其实就已经定义了类的两个属性,他们是在构造方法中以直接赋值的方式定义的.因此,Python中服你定义属性就是直接使用它,可以在构造方法中定义属性,也可以在类中的其他方法使用定义属性.

            Python语言中类的属性有两种:

  • 实例属性
  • 类属性
实例属性即同一个类的不同实例,其值是不相关联的,也不会互相影响的,定义时使用"self.属性名",调用时也使用它;类属性则是同一个类的所有实例所共有的,直接在类体中独立定义,引用是要使用"类名.变量名"形式来引用,只要是某个实例对其进行修改,就会影响其他的所有这个类的实例.

        [实例]演示了类中定义的类属性和实例属性的定义和使用,代码如下:

class Demo_Property:                    #定义类
    class_name = "Demo_Property"        #类属性
    def __init__(self, x=0):            #实例属性
        self.x = x

    def class_info(self):               #输出信息的方法
        print('类变量值:', Demo_Property.class_name)
        print('实例变量值:',self.x)

    def chng(self,x):                   #修改实例属性的方法
        self.x = x                      #注意实例属性的引用方式

    def chng_cn(self,name):             #修改类属性的方法
        Demo_Property.class_name = name #注意类属性引用方式
dpa = Demo_Property()                   #实例化类
dpb = Demo_Property()                   #实例化类
print("初始化两个实例")
dpa.class_info()
dpb.class_info()
print("修改实例变量")
print('修改dpa实例变量')
dpa.chng(3)
dpa.class_info()
dpb.class_info()
print('修改dpb实例变量')
dpb.chng(10)
dpa.class_info()
dpb.class_info()
print('修改类变量')
print("修改dpa类变量")
dpa.chng_cn('dpa')
dpa.class_info()
dpb.class_info()
print('修改dpb类变量')
dpb.chng_cn('dpb')
dpa.class_info()
dpb.class_info()

[代码说明]代码中首先定义了一个类Demo_Property,类具有一个类属性 class_name和一个实例属性x,以及两个分别修改实例属性和类属性的方法.其次分别实例化这个类,并调用这两个实例来修改类属性和实例属性.

[运行效果]如图,对于实例属性来说,两个实例之间互不联系,他们各自可以被修改为不同的值;对于类属性来说,无论哪个实例修改了它,会导致所有实例类属性的值发生变化.

初始化两个实例
类变量值: Demo_Property
实例变量值: 0
类变量值: Demo_Property
实例变量值: 0
修改实例变量
修改dpa实例变量
类变量值: Demo_Property
实例变量值: 3
类变量值: Demo_Property
实例变量值: 0
修改dpb实例变量
类变量值: Demo_Property
实例变量值: 3
类变量值: Demo_Property
实例变量值: 10
修改类变量
修改dpa类变量
类变量值: dpa
实例变量值: 3
类变量值: dpa
实例变量值: 10
修改dpb类变量
类变量值: dpb
实例变量值: 3
类变量值: dpb
实例变量值: 10

有时为了不让某个属性或方法在类外被调用或者修改,可以使用'__'双下划线的名称,但是这并不保证一定不能从类外调用,它只是一种标志,其实Python不提供类似其他面向对象语言中的私有属性和方法,你在编程时形成良好的习惯就可以了.

类成员方法和静态方法

   类的属性有类属性和实例属性之分,类的方法也有不同的种类,主要有:

  • 实例方法
  • 类方法
  • 静态方法
  • 前文中定义的所有类中的属性的方法都是实例方法,其隐含调用参数是类的实例.类方法隐含调用参数则是类,静态方法没有隐含调用参数.类方法和静态方法的定义方式与实例方法不同,他们的调用方式也不同.

    静态方法定义时应使用修饰器@staticmethod 进行修饰,是没有默认参数的.类方法定义时应使用修饰器@classmethod 进行修饰,必须有默认参数"cls".他们的调用方式可以直接有类名进行调用,调用前也可以不实例化类,当然也可以用该类的任一个实例来进行调用.

[实例]此示例演示了一个同时定义了类方法和静态方法的类,代码如下:

                            

class Demomthd:                     #定义一个类

    @staticmethod                   #静态方法的装饰器
    def static_mthd():              #静态类定义
        print("调用了静态方法!")
    @classmethod                    #类方法的装饰器
    def class_mthd(cls):            #类方法定义,带默认参数c1s
        print("调用了类方法!")
Demomthd.static_mthd()              #未实例化类,通过类名调用静态方
Demomthd.class_mthd()               #未实例化类,通过类名调用类方法
dm = Demomthd()                     #实例化类
dm.static_mthd()                    #通过类实例调用静态方法
dm.class_mthd()                     #通过类实例调用类方法

[代码说明]代码定义了一个同时具有静态方法和类方法的类DemoMthd,然后在未实例化是用类名来调用他们;实例化后用类实例来调用它们.

[运行效果]如图,两种方式调用都是正确的.

>>>
调用了静态方法!
调用了类方法!
调用了静态方法!
调用了类方法!

注意:在静态方法和类方法中不能使用实例属性,因为可能调用时类还没有实例化.

类的继承

面向对象的编程的最大优点之一就是可以通过继承来减少代码,同时也可以灵活定制新类.

1.类的继承

        类是可以继承的,子类继承父类后,就具有了父类的属性和方法,但不能继承父类的私有属性和私有方法(属性名或方法前缀为两个下划线的),子类中还可以重载来修改父类的方法,以实现与父类行为表现或能力.

        [实例]演示了类的继承,代码如下:

class Ant:                          #定义类Ant

    def __init__(self,x=0,y=0,color='black'):
        self.x = x                  #定义构造方法
        self.y = y
        self.color = color
    def crawl(self,x,y):            #定义方法(模拟爬行)
        self.x = x
        self.y = y
        print("爬行...")
        self.info()

    def info(self):
        print('当前位置: (%d,%d)' %(self.x,self.y))

    def attack(self):               #定义方法(模拟攻击)
        print("用嘴咬!")

class FlyAnt(Ant):                  #定义FlyAnt类,继承Ant类

    def attack(self):               #修改行为(攻击方法不同)
        print("用尾针!")

    def fly(self, x, y):            #定义方法(模拟飞行)
        print("飞行...")
        self.x = x
        self.y = y
        self.info()

flyant = FlyAnt(color='red')        #实例化类
flyant.crawl(3, 5)                  #调用方法(模拟爬行)
flyant.fly(10, 14)                  #调用方法(模拟飞行)
flyant.attack()                     #调用方法(模拟攻击)

[代码说明]代码中首先定义了父类Ant,具有爬行和嘴攻击能力,然后定义了继承Ant类的子类FlyAnt,从Ant类中继承类爬行的能力,添加了飞行的能力,修改了攻击方式.之后实例化FlyAnt类,实例化调用其爬行,飞行以及进攻方法.

[运行效果]如图,父类具有爬行,攻击能力,子类添加了飞行能力,修改了进攻方式.

爬行...
当前位置: (3,5)
飞行...
当前位置: (10,14)
用尾针!

2.多重继承

        在面向对象编程的语言中,有的允许多重继承,即一个类可以继承多个类;有的只允许单一继承,即一个类只能继承一个类,而Python中则允许多重继承.

        多重继承的方式是在类定义时的继承父类的括号中,以','分隔开要多重继承的父类即可.而多重继承时.继承顺序也是一个很重要的要素,如果继承的多个父类中有相同的方法名,但在类中使用时未指定父类名,则Python解释器将从左至右搜索,即调用先继承的类中的同名方法.

        [实例]演示了一个多重继承的实例,代码如下:

class PrntA:                        #定义父类PrntA

    namea = 'PrntA'
    def set_value(self,a):
        self.a = a

    def set_namea(self,namea):
        PrntA.namea = namea

    def info(self):
        print('prntA:%s,%s' %(PrntA.namea,self.a)
class PrtnB:                        #定义父类PrntB
    nameb = "PrntB"

    def set_nameb(self,nameb):
        PrntA.nameb = nameb

    def info(self):
        print('PrntB:%s' %(PrtnB.nameb,))

class Sub(PrntA,PrtnB):             #定义子类Sub,先后继承了 PrntA, PrntB
    pass

class Sub2(PrtnB,PrntA):           #定义子类sub,先后继承了 PrntB, PrntA
    pass

class Sub3(PrntA,PrtnB):            #定义子类Sub,先后继承了 PrntA, PrntB

    def info(self):                 #修改了方法info
        PrntA.info(self)
        PrtnB.info(self)

print('使用第一个子类:')
sub = Sub()                         #实例化类Sub
sub.set_value('aaaa')
sub.info()
sub.set_nameb('BBBB')
sub.info()
print('使用第二个子类:')
sub2 = Sub2()                       #实例化类Sub2
sub2.set_value('aaaa')
sub2.info()
sub2.set_nameb('BBBB')
sub2.info()
print("使用第三个子类:")                #实例化类Sub3
sub3 = Sub3()
sub3.set_value('aaaa')
sub.info()
sub3.set_nameb('BBBB')
sub.info()

【代码说明】代码中定义了两个父类 PrntA和PrntB,它们有一个同名的方法info()用于输出类的相关信息。第一个子类Sub先后继承了 PrntA,PrntB,实例化后,先调用了PrntA中的方法,之后调用了info()方法,由于两个父类中有同名的方法info(),所以实际上调用了PrntA中的info()方法,因此只输出了从父类PrntA中的继承的相关信息。第二个子类Sub2继承的顺序相反,当调用info()方法时,实际上调用的是属于PrntB中的info()方法,因此只输出从父类PrntB中的继承的相关信息。第三个子类Sub3继承的类及顺序和第一个子类Sub相同,但是修改了父类的info0方法,在其中分别调用了两个父类的info()方法,因此,每次调用Sub3类实例的info()方法,两个被继承的父类中的信息都输出了。

[运行效果]如图,使用第一,第二个子类时,两次调用info()方法时,仅输出了其中的一个父类的信息,使用第三个子类时,每次调用info()方法时,同时输出了两个父类的信息.

>>>
使用第一个子类时:
PrntA:PrntA,aaaa
PrntA:PrntA,aaaa
使用第二个子类时:
PrntB:PrntB
PrntB:PrntB
使用第三个子类时:
PrntA:PrntA,aaaa
PrntB:PrntB
PrntA:PrntA,aaaa
PrntB:PrntB

3.方法重载

             当子类继承父类时,子类如果要想修改父类的行为,则应使用方法重载来实现,方法重载的基本方法是在子类中定义一个和所继承的父类中需要重载方法同名的一个方法即可。
           例如在前文中实例中子类 FlyAnt继承了父类Ant,父类中已经定义了方法 attack(),而子类中也定义了一个 attack()方法。即 attack()方法被重载了。当子类实例调用 attack()方法时,就会直接调用子类中的 attack()方法,而不会调用父类中同名的方法。
            再如在前文中实例的多重继承中,两个父类都具有同名方法info(),但在子类中也定义了一个info()方法,即 info()方法被重载了。当子类实例调用 info()方法时,就会直接调用该实例中定义的info()方法,而不会去调用任何一个父类的info()方法。

 
       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值