Logo

郎哥编程

类与对象

2020-12-26 155

封装就是把现实世界同类事物的共同特征和行为抽取出来,放到一个新建的Python类中,并设置类属性(特征)和行为,同时提供外部访问类属性和行为的方法。

类是把同类事物的共同特征和行为封装在一起的结构体,事物的特征称为属性,事物的行为称为方法,类是抽象的概念集合,表示的是一个共性的产物,类中定义的是属性和方法。例如前面创建的Fruits类(水果类),Fruits类有属性water(汁液含量)、sugar(糖分含量)、fragrance(芳香度),这些属性是水果类所具有的共同特点:汁液含量丰富、糖分含量大、有一定程度的芳香气味。Fruits类也有方法类获取类属性和设置类属性的方法。

定义一个Python类

在Python中,通过关键字class来声明类,类定义语法如下:

#定义一个类
class 类名:
    #定义类属性部分
    属性名称1=值1;
    属性名称2=值2;
    ……
    属性名称n=值n;
   
    #构造方法
    def __init__(self):
      类初始化语句;
    #定义类的其它方法
    def method1():
      方法语句块
    def method2():
      方法语句块
    ……
    def methodn():
      方法语句块

一个完整的Python类由类声明和类主体构成,类主体内容放在在类声明之后。

类声明语句为“class  类名”,其中class是Python预定义的关键字,声明Python类时,类声明需要包含class关键字。

类属性的定义和在程序中创建变量是相同的语法,类方法的定义和在程序中定义函数是相同的语法。

在声明类属性和方法时,可以声明类属性和方法的访问权限。访问权限分为public(公共访问权限)、protected(保护访问权限)、private(私有权限)。声明为public权限的类属性和方法可以允许外部直接访问该属性或调用该方法;声明为protected权限的类属性和方法只能允许类本身与子类访问和调用;声明为private权限的类属性和方法只能允许类本身访问和调用,其继承的子类也没有权限访问和调用。

Python默认的类属性和方法都是public权限,也就是在属性和方法前面不加任何修饰符;在属性和方法前面加单下划线“_”修饰符,可以修饰该属性或方法为protected权限;在属性和方法前面加双下划线“__”修饰符,可以修饰该属性或方法为private权限。

在名称前后均有双下划线“__”的属性或方法,定义的是特殊属性或方法。如上面代码中的构造方法__init__,这些属性或方法一般已经在Python中定义。程序可以重写方法和修改属性的值,以便Python调用它们。

类的属性和方法也称为类属性变量和类方法。

__init__(self)方法是类的构造方法,该方法在类被实例化对象时会自动调用,类属性的初始化可以放置在该方法内。该方法的第1个参数是self,表示创建的类实例本身,在创建类实例的时候,不需要传入self参数,self参数由Python解释器隐含传入。

由于self表示当前类的实例对象,因此在类的内部可以使用self直接访问类的属性和方法。

案例1:定义一个日期类

#声明一个日期类
class Date:
    #定义类属性
    year = ""
    month = ""
    day = ""
    #定义方法
    #构造函数
    def __init__(self, year,month,day):
        self.year = year
        self.month = month
        self.day = day
    #判断是否是闰年
    def isRunnian(self):
        if (self.year % 4 == 0 and self.year % 100 != 0) or (self.year % 400 == 0):
            return True
        else:
            return False
    #输出日期
    def printDate(self):
        print("%s-%s-%s" % (self.year,self.month,self.day))

定义的日期类有year、month、day三个属性,用来存储日期的年、月、日。另外,日期类还提供了isRunnian()和printDate ()两个方法,isRunnian()用于判断当前日期是否是闰年,printDate ()用于输出当前日期。

日期类的__init__构造方法有4个参数,除去self参数外,另外3个参数分别是year(年), month(月),day(日),在实例化对象时需要传入这三个参数。

日期类的属性名称与__init__构造方法中的形参名称相同,为了不引起类属性变量和形参局部变量的混淆。在这种情况下,可以使用self来指定类的属性变量。

self.year = year语句的self.year指定的是Date类的属性变量year,语句赋值运算符右侧的year是__init__构造方法传入的实参year。

在定义Python类时,还有一个简洁的方法。不用显示定义类的属性,在类的方法中直接定义类的属性即可。

#声明一个日期类
class Date:
    #构造函数
    def __init__(self, year, month,day):
        self.year = year;
        self.month = month;
        self.day = day;
    #判断是否是闰年
    def isRunnian(self):
        if (self.year % 4 == 0 and self.year % 100 != 0) or (self.year % 400 == 0):
            return True;
        else:
            return False;
    #输出日期
    def printDate(self):
        print("%s-%s-%s" % (self.year,self.month,self.day))

Date类没有显示定义类的属性变量,在__init__构造方法内可以直接使用self来定义类的属性变量。

类实例化为对象

类是抽象的概念集合,表示的是一个共性的产物,类中定义了类的属性和方法,而对象是类的实例。

例如前面建立的水果类,水果被归纳为类,而苹果、香蕉、葡萄为水果类的实例或对象。水果是人们赋予具有苹果、香蕉、葡萄等共同特点的名称,不单指某一事物;对象是指具体的实物或概念,如苹果、香蕉、葡萄等对象是实物,而一项政策可能就是一个概念性的对象了,在现实生活中,万事万物皆对象,面向对象编程就是模拟现实生活中的一个个对象来编程的。

类也可以看做是对象的模板,它描述一类对象的行为和状态,决定着对象的属性和方法。由对象可以抽象出类,类也可以实例化成对象,就像水果类决定了苹果、香蕉、葡萄等对象具备糖分、汁液、芳香度都基本特征,也可以通过抽取香蕉、葡萄等对象的共同特征抽象为水果类。

29.png

由苹果、葡萄等对象抽象出水果类,水果类属性有water(汁液含量)、sugar(糖分含量)、fragrance(芳香度),这些属性是水果类所具有的共同特点。当在程序中需要使用苹果对象时,需要将水果类实例化苹果,同时初始化苹果对象的water(汁液含量)、sugar(糖分含量)、fragrance(芳香度)属性。

类实例化为对象

对象是根据类创建的。在Python中,将类实例化为对象非常简单。实例化对象语法如下:

对象名 = 类名()

例如:实例化Fruits类为apple对象

apple = Fruits("80%","30%","20%")

再如:实例化Date类为date对象

date = Date(2020,12,20)

Date类实例化date对象时,会调用Date类的构造方法初始化对象,Date类的构造方法要求传入year(年)、month(月)、day(日)三个实参,初始化对象的属性。

Date类的构造方法代码如下:

def __init__(self, year,month,day):
        self.year = year
        self.month = month
        self.day = day

其中,self表示类实例(对象)本身,在创建实例的时候self不需要传入,Python解释器会自己把实例对象传进去。

访问对象的属性和方法

程序访问对象中封装好的属性和方法是在对象名称后面加“.”操作符进行的。例如:

#实例化Date对象      

date = Date(2020,12,20);

#调用Date对象的printDate()方法

date.printDate()

#调用Date对象的isRunnian()方法

print(date.isRunnian())

如果对象的属性是public权限(默认是public访问权限),可以直接对对象的属性进行赋值。

#实例化Date对象      
date = Date(2020,12,20);
#设置对象属性year的值为2019
date.year = 2019
#设置对象属性month的值为10
date.month=10
#设置对象属性day的值为30
date.day = 30

如果对象的属性不是public访问权限,外部会无法访问和设置对象的属性,如果需要从外部获取或设置对象的属性,可以在类中添加属性的get和set方法。

#声明一个日期类
class Date:
    #构造函数
    def __init__(self, year, month,day):
        self.__year = year;
        self.month = month;
        self.day = day;
    #判断是否是闰年
    def isRunnian(self):
        if (self.__year % 4 == 0 and self.__year % 100 != 0) or (self.__year % 400 == 0):
            return True;
        else:
            return False;
    #输出日期
    def printDate(self):
        print("%s-%s-%s" % (self.__year,self.month,self.day))
 
    # __year属性的get方法
    def getYear(self):
        return self.__year
    # __year属性的set方法
    def setYear(self,year):
        self.__year = year

Date类的属性__year是private权限,外部无法获取和设置__year属性的值,因此在Date类定义了getYear(self)方法,该方法返回__year属性的值,也定义了setYear(self,year)方法,该方法设置__year的值。

案例2:参照课程内容定义一个Fruits水果类,并实例化Fruits类为apple对象,然后调用Fruits类的showFruit()方法输出Fruits对象的属性。

#定义水果类
class Fruits:
    #水果类汁液含量属性
    water = ""
    #水果类糖分含量属性
    sugar= ""
    #水果类芳香度属性
    fragrance = ""
    #水果类构造方法(函数)
    def __init__(self, water, sugar,fragrance):
        self.water = water
        self.sugar = sugar
        self.fragrance = fragrance
   
    def getWater(self):
        return self.water
    def getSugar(self):
        return self.sugar
    def getFragrance(self):
        return self.fragrance
   
    def setWatet(self,inwater):
        self.water = inwater
    def setSugar(self,insugar):
        self.sugar = insugar
    def setFragrance(self,infragrance):
        self.fragrance = infragrance
    def showFruit(self):
        print("汁液含量:%s" % (self.water))
        print("糖分含量:%s" % (self.sugar))
        print("芳香度:%s" % (self.fragrance))
 
#实例化苹果对象      
apple = Fruits("80%","30%","20%")
#调用showFruit输出苹果对象属性
apple.showFruit()

案例代码定义了Fruits类。Fruits类有汁液含量(water)、糖分含量(sugar)和芳香度(fragrance)类属性,并提供了类属性的设置或获取方法,也提供了showFruit方法,该方法使用print语句输出类的属性。

在类的定义后面,使用Fruits类实例化apple对象,在类的实例化过程中,会调用Fruits类的构造方法,并传入初始化对象属性的参数,最后调用apple对象的showFruit()方法输出对象属性。

Python对象是Python类的实例化,Python类的实例化就是在代码中定义一个类型为Python类的变量,然后调用类的构造方法申请Python类的存储空间并初始化Python类的属性,被初始化的Python类赋值给前面已声明的变量,该变量即为Python类的实例化对象。

使用内置函数操作对象的属性

1、访问对象的属性

函数声明:

getattr(object, name[,default])

函数返回对象命名属性的值。object是一个已实例化的对象,实参name 必须是字符串。如果该字符串是对象的属性之一,则返回该属性的值。

例如, getattr(x, 'foobar') 等同于 x.foobar。如果指定的属性不存在,且提供了 default 值,则返回default,否则触发 AttributeError。

2、判断对象的属性是否存在

函数声明:

hasattr(object, name)

函数判断对象object是否具有name属性。object是一个已实例化的对象,实参name 必须是字符串,如果name是对象的属性之一的名称,则返回 True,否则返回 False。

3、设置对象的属性

函数声明:

setattr(object, name, value)

函数将对象object的name属性值设置为value。object是一个已实例化的对象,实参name 必须是字符串,如果name是对象的属性之一的名称,函数会将value赋值给该属性。

例如,setattr(x, 'foobar', 123) 等价于 x.foobar = 123。

类方法的不同称谓及作用

访问类的方法和属性可以通过类的实例对象来访问,但在一些情况下,也可以直接通过类名来访问。因为类的方法和属性有不同的访问方式,因此根据访问方式的不同,类的方法和属性也有不同的称谓。

按照访问方式的不同,类方法分为构造方法、实例方法(或者实例对象方法,或对象方法)、类方法、静态方法和析构方法。

(1)构造方法

类被实例化时会自动调用的方法称为类的构造方法,构造方法主要完成类的初始化工作。不同于java和C++编程语言,定义Python类时,不需要提供显示的构造方法,类的__init__(self)方法代替了构造方法的初始化工作,在类被实例化的过程中,__init__(self)方法会被自动调用。

在Python中,类中返回一个实例对象的方法,也称为构造方法。这样的构造方法属于类方法的一种,通过类名来调用方法。

案例代码:

class Date(object):
    #构造方法
    def __init__(self, year,month,day):
        self.year = year
        self.month = month
        self.day = day
 
    #构造方法
    @classmethod
    def toDay(cls,year,month,day):
        return Date(year,month,day)
   
    #实例方法
    def showDate(self):
        print("%d-%d-%d" % (self.year,self.month,self.day))
 
d = Date.toDay(2020,8,17)
d.showDate()

上面的案例代码,定义了toDay()方法,该方法返回一个Date实例对象,它是一个构造方法。该方法需要使用类名直接调用,因此在类方法声明的前面一行添加了@classmethod修饰符(修饰符也称为装饰器),使用@classmethod修饰符修改的类方法可以直接使用类名来调用,不需要实例化对象后才能调用。

使用@classmethod修饰符修饰的方法,第一个参数必须是示自身类的 cls 参数,cls也可以是其它名称,类似于self。

(2)类方法

前面介绍构造方法时,已经说到了类方法。类方法可以通过类名来直接调用,不需要实例化对象后调用。

要定义一个方法为类方法,需要在类方法声明的前面一行添加修饰符:

@classmethod

其中@是修饰符的前缀字符,classmethod是类方法的修饰符。

案例代码:

class Calculator(object):
    #构造方法
    def __init__(self, op1,op2):
        self.op1 = op1
        self.op2 = op2
    #类方法
    @classmethod
    def add(cls,op1,op2):
        return op1 + op2
   
if __name__=="__main__":
    print(Calculator.add(13,7))

案例代码定义了类方法add(),在程序中可以直接使用类名Calculator来调用add()方法计算两数的和。

(3)静态方法

Python解释器在类实例化对象的过程中,会为实例对象分配内存,类的属性和方法也被分配了内存,此时可以通过实例对象调用类的方法和访问类的属性。

在实际应用中,一些类被设计为工具或功能类,这些类的方法可以完成一些特定的计算或功能。在这种情况下,可以把类的方法定义为静态方法,直接通过类名来调用,提高了类的使用效率。

静态方法在模块导入类的过程中,就已经被分配了内存,因此静态方法可以通过类名直接调用,无需实例化对象后再调用。

要定义一个方法为静态方法,需要在类方法声明的前面一行添加修饰符:

@staticmethod

其中@是修饰符的前缀字符,staticmethod是静态方法的修饰符。

案例代码:

class Area(object):
  #类的静态方法
  @staticmethod
  def circle(r):
    return 3.14 * r * r
   
if __name__=="__main__":
  print(Area.circle(6))

Area类用来计算几何图形的面积,当前提供了圆面积的计算方法circle(),circle()方法被定义为静态方法,该方法在程序中可以直接通过Area类名来调用。

(4)实例方法

实例方法声明前面不添加任何修饰符,类的实例对象才能调用实例方法,通过类名不能调用实例方法。

(5)析构方法

object类有一个方法__del__(self),该方法在实例对象被释放时调用,用于释放类自身的资源,该方法也称为析构方法。

当自定义类需要释放资源时,需要重写object类的__del__(self)方法,释放自身的资源。

案例代码:

class TextFile(object):
    #构造方法
    def __init__(self,filepath):
        self.filepath = filepath
        self.fp = None
 
    #打开文件方法
    def open(self):
        try:
            #使用r模式打开文本文件,编码方式为utf-8
            self.fp = open(self.filepath,"r",encoding='utf-8')
        except IOError:
            print("文件打开失败,%s文件不存在" % filename)
 
    #析构方法
    def __del__(self):
        # 若文件被打开,关闭文件
        if self.fp:
             print("文件被关闭")
             self.fp.close();      
   
if __name__=="__main__":
 
   textfile = TextFile("d://test.txt")
   textfile.open()
   # 删除实例对象textfile
   del textfile;
案例代码定义了TextFile类,该类准备封装文本文件的操作,当前仅提供了open(self)方法,用于打开文件,同时重写了object类的析构方法__del__(self),在析构方法内部关闭已经打开的文本文件。

案例代码语句:

del textfile

删除实例对象textfile,del是Python的删除语句,用于删除实例对象。

5、 类属性的不同称谓及作用

类属性按照访问方式的不同,分为类属性和实例属性。类属性可以通过类名直接访问,实例属性只能通过实例对象访问。

类属性在类体中直接定义,实例属性在__init__(self)方法中通过self来定义。

案例代码:

class Fruits:
    #类属性
    water = "0%"
  
    #水果类构造方法(函数)
    def __init__(self, water, sugar,fragrance):
 
        # 使用类名访问类属性
        Fruits.water = water
        # 实例属性
        self.sugar = sugar
        # 实例属性
        self.fragrance = fragrance
   
    def showFruit(self):
        print("汁液含量:%s" % (Fruits.water))
        print("糖分含量:%s" % (self.sugar))
        print("芳香度:%s" % (self.fragrance))
 
if __name__=="__main__":
 
    # 使用类名直接访问类属性
    print(Fruits.water)
    # 实例化Fruits对象
    apple = Fruits("80%","30%","20%")
    # 实例属性sugar只能允许实例对象访问
    apple.showFruit()
案例代码定义了Fruits类,其中water在类体中定义,是类属性。sugar和fragrance在类__init__(self)方法中定义,是实例属性。

访问类属性时,需要在类属性前面添加类名称和一个圆点:

Fruits.water

需要注意的是,若在__init__(self)方法中也定义了self.water,则self.water和类体定义的water不是同一个属性,self.water隶属于实体对象属性,water隶属于Fruits类属性。

代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论