6.类
6.1类和对象
6.1.1类的定义
计算机语言中有2个重要概念:数据、算法。在Python3中,数据可以使用数字、序列、集合、字典来表示;算法可以用函数/方法来实现。为了将数据和算法统一管理,于是引申出了类。
Python3中,类中的数据称为变量(注:也有人称为属性),包括类变量和实例变量;类中算法的实现称为方法,包括类方法、实例方法、静态方法和专有方法。
类定义格式如下:
class Classname(object):
pass
其中,使用关键字class来定义一个类,Classname表示类的名称,object表示该类的父类/基类。
在Python3中,变量都是小写,单词之间使用_来连接;常量都是大写;类名是首字母大写,单词之间使用首字母大写来区分。这些都是程序员默认的书写规则。
若该类无父类,则括号()和object可以不写,即格式修改如下:
class Classname:
pass
或
class Classname(): #建议采用此格式
pass
#程序6-1
class Person():
name = ''
age = 0
def print_info(self):
print('this is a test')
p1 = Person()
p1.print_info()
运行结果:
this is a test
6.1.2创建对象
类是一个抽象性的概念。类的定义就是将所有对象的共性提取出来,如Person类中的姓名和年龄是所有人都会有的。
对象是一个具体的实体。创建对象可以理解为类实例化的过程,将Person类实例化出一个具体的人,如姓名’zhangsan’、年龄18。
#程序6-2
class Person():
#this is a test
name = ''
age = 0
def __init__(self,name = 'none',age = 0):
self.name = name
self.age = age
def print_info(self):
print('name :'+ self.name)
print('age :'+ str(self.age))
p1 = Person()
p1.print_info()
p2 = Person('zhangsan',18)
p2.print_info()
运行结果:
name :none
age :0
name :zhangsan
age :18
在Python3中,使用”object = Classname(parameters)”的形式来创建对象。在其他编程语言中如C++,会使用”Classname object(parameters)”这样的形式来创建对象,但是这在Python3中是完全不行的。
原因:在Python3中,变量、对象是没有数据类型的,因此必须使用赋值运算符(=)的形式来给变量、对象赋值;而使用Classname object(parameters)的形式来创建对象,则对象的数据类型会被限制为Classname。
6.2变量和方法
在Python3中,对象或类使用点运算符(.)来访问类中的变量和方法。
6.2.1实例方法
在类中定义的实例方法,其第一个参数都必须是self;必须使用对象才可以调用。
#程序6-3
class Person():
#this is a test
name = ''
age = 0
def __init__(self,name = 'none',age = 0):
self.name = name
self.age = age
def print_info(self,profession):
print('name :'+ self.name)
print('age :'+ str(self.age))
print('profession :'+ profession)
p1 = Person()
p1.print_info('baby')
p2 = Person('zhangsan',18)
p2.print_info('student')
运行结果:
name :none
age :0
profession :baby
name :zhangsan
age :18
profession :student
程序6-3中的方法print_info(self,profession)就是实例方法,其必须是实例化的对象才可以调用。注:类无法调用,如执行Person.print_info('student')就会出错。
实例方法中的self到底是什么?
#程序6-4
class Person():
#this is a test
name = ''
age = 0
def __init__(self,name = 'none',age = 0):
self.name = name
self.age = age
def print_info(self,profession):
print('name :'+ self.name)
print('age :'+ str(self.age))
print('profession :'+ profession)
print(self)
p1 = Person()
p1.print_info('baby')
p2 = Person('zhangsan',18)
p2.print_info('student')
运行结果:
name :none
age :0
profession :baby
<__main__.Person object at 0x00E90910>
name :zhangsan
age :18
profession :student
<__main__.Person object at 0x00E90930>
在实例方法中,self指的就是对象本身。当执行p1.print_info('baby')时,self就是p1;执行p2.print_info('student')时,self就是p2。
在类的方法定义中,self可以换成其它名称,只要符合变量的定义即可;但是Python3默认规定是使用self或this。
6.2.2专有方法-构造函数和析构函数
类的专有方法是在特殊情况下或使用特殊语法时,由Python主动调用;其形式一般是__xxx__。
构造函数和析构函数是类的专有方法。当创建对象时,会默认调用构造函数;当删除对象时,会默认调用析构函数。构造函数的格式:def __init__(self, parameter...);析构函数的格式:def __del__(self)。
#程序6-5
class Person():
#this is a test
name = ''
age = 0
def __init__(self,name = 'none',age = 0):
print('__init__')
self.name = name
self.age = age
def __del__(self):
print('__del__')
def print_info(self):
print('name :'+ self.name)
print('age :'+ str(self.age))
p1 = Person() #调用构造函数
p1.print_info()
p2 = Person('zhangsan',18) #调用构造函数
p2.print_info()
运行结果:
__init__
name :none
age :0
__init__
name :zhangsan
age :18
__del__
__del__
构造函数可以使用对象直接调用;不可以使用类来调用。
#程序6-6
class Person():
#this is a test
name = ''
age = 0
def __init__(self,name = 'none',age = 0):
print('__init__')
self.name = name
self.age = age
def __del__(self):
print('__del__')
def print_info(self):
print('name :'+ self.name)
print('age :'+ str(self.age))
p1 = Person()
p1.__init__('lisi',16)
p1.print_info()
p2 = Person('zhangsan',18)
p2.print_info()
运行结果:
__init__
__init__
name :lisi
age :16
__init__
name :zhangsan
age :18
__del__
__del__
在程序6-6中,由于使用了p1.__init__('lisi',16),因此多调用了一次构造函数。
析构函数也可以用对象来调用;或使用del关键字。
#程序6-7
class Person():
#this is a test
name = ''
age = 0
def __init__(self,name = 'none',age = 0):
print('__init__')
self.name = name
self.age = age
def __del__(self):
print('__del__')
def print_info(self):
print('name :'+ self.name)
print('age :'+ str(self.age))
p1 = Person()
p1.print_info()
p2 = Person('zhangsan',18)
p2.print_info()
p3 = Person('lisi',16)
p3.print_info()
print(p1.__dict__)
p1.__del__()
print(p1.__dict__)
print(p2.__dict__)
del p2
print(p2.__dict__)
print(p3.__dict__)
运行结果:
__init__
name :none
age :0
__init__
name :zhangsan
age :18
__init__
name :lisi
age :16
{'name': 'none', 'age': 0}
__del__
{'name': 'none', 'age': 0}
{'name': 'zhangsan', 'age': 18}
__del__
Traceback (most recent call last):
File ".\run.py", line 32, in <module>
print(p2.__dict__)
NameError: name 'p2' is not defined
__del__
__del__
当执行p1.__del__()会调用类Person的析构函数,但是析构函数并没有对p1对象做任何处理,因此在第二次使用__dict__时依旧不变。这也是在程序执行完后,打印了4次__del__的原因,因为p1对象并没有被删除。
当执行del p2时也调用了类Person的析构函数,但是del删除了p2对象,因此使用__dict__时会报出变量不存在的错误。注:__dict__是一个字典,其存放着对象的变量,key表示变量名,value表示变量值。
总结:
(1)对象删除一定调用析构函数;调用析构函数并不一定删除对象。
(2)在构造函数和析构函数中,self指的也是对象。
(3)我们应该遵循”构造函数和析构函数的调用次数相等”,因此不要使用对象来调用构造函数和析构函数。在创建对象时,会有Python主动调用构造函数;当程序执行完或使用del关键字时,会有Python主动调用析构函数。
(4)构造函数和析构函数是无返回值的。
6.2.3类变量和实例变量
类变量:属于类的变量,可以使用类名调用;不属于对象的变量,无法使用对象来调用。
实例变量:属于对象的变量,可以使用对象来调用;不属于类的变量,无法使用类名调用。
#程序6-8
class Person():
#this is a test
count = 0 #类变量
def __init__(self,name = 'none',age = 0):
print('__init__')
self.name = name #实例变量
self.age = age #实例变量
def __del__(self):
print('__del__')
def print_info(self):
print('name :'+ self.name)
print('age :'+ str(self.age))
p1 = Person('zhangsan',18)
print(Person.__dict__)
print(p1.__dict__)
运行结果:
__init__
{'__module__': '__main__', 'count': 0, '__init__': <function Person.__init__ at 0x03A096F0>, '__del__': <function Person.__del__ at 0x03A09618>, 'print_info': <function Person.print_info at 0x03A097C8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'name': 'zhangsan', 'age': 18}
__del__
在类Person的变量打印中,出现count,因此count是类变量;在对象p1的变量打印中,出现name和age,因此name和age是实例变量。
#程序6-9
class Person():
#this is a test
count = 0 #类变量
def __init__(self,name = 'none',age = 0):
print('__init__')
self.name = name #实例变量
self.age = age #实例变量
def __del__(self):
print('__del__')
def print_info(self):
print('name :'+ self.name)
print('age :'+ str(self.age))
p1 = Person('zhangsan',18)
p1.abc = 'object variable'
Person.xyz = 'class variable'
print(Person.__dict__)
print(p1.__dict__)
运行结果:
__init__
{'__module__': '__main__', 'count': 0, '__init__': <function Person.__init__ at 0x01580A08>, '__del__': <function Person.__del__ at 0x016696F0>, 'print_info': <function Person.print_info at 0x016696A8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, 'xyz': 'class variable'}
{'name': 'zhangsan', 'age': 18, 'abc': 'object variable'}
__del__
我们发现使用点运算符(.)不仅可以访问类中定义的变量和方法,还可以构造变量。当使用点运算符访问变量时,若此变量已定义则直接访问;若未定义则构造变量。若访问者是类名,则构造类变量;若是对象,则构造实例变量。
总结:类的定义中,在方法外定义的变量以及使用类名和点运算符构造的变量是类变量;使用对象和点运算符构造的变量是实例变量。
实例变量的调用:类的方法中,使用self来调用;类外,使用对象来调用。
类变量的调用:使用类名来调用,即class_name.variable;使用__class__来调用,即object.__class__.variable。
#程序6-10
class Person():
#this is a test
count = 0 #类变量
def __init__(self,name = 'none',age = 0):
print('__init__')
self.name = name #实例变量
self.age = age #实例变量
Person.count += 1
# self.__class__.count += 1
def __del__(self):
print('__del__')
Person.count -= 1
def print_info(self):
print('name :'+ self.name)
print('age :'+ str(self.age))
print('count :'+ str(self.__class__.count))
p1 = Person()
p1.print_info()
p2 = Person('zhangsan',18)
p2.print_info()
p3 = Person('lisi',12)
p3.print_info()
运行结果:
__init__
name :none
age :0
count :1
__init__
name :zhangsan
age :18
count :2
__init__
name :lisi
age :12
count :3
__del__
__del__
__del__
代码中,count用来表示对象创建的个数。当调用构造函数时,count加1;调用析构函数时,count减1。
6.2.4类方法
前面学习了类变量和实例变量,类变量由类来调用,实例变量由对象来调用。那么在方法中,是否有一种方法专门由类来调用?这就是本节要讲的类方法。
类方法格式:
@classmethod
def funcname(cls, parameter_list):
pass
其中,classmethod是装饰器,表示这个方法是类方法;cls表示类名,也可以换成其它名称,但是在Pyhton3种默认规定使用cls。
类方法一般由类来调用,其类名作为第一个参数传入类方法的cls参数中。
#程序6-11
class Person():
#this is a test
count = 0 #类变量
def __init__(self,name = 'none',age = 0):
print('__init__')
self.name = name #实例变量
self.age = age #实例变量
Person.count += 1
# self.__class__.count += 1
def __del__(self):
print('__del__')
Person.count -= 1
def print_info(self):
print('name :'+ self.name)
print('age :'+ str(self.age))
@classmethod
def get_count(cls): #类方法
print('count :'+ str(cls.count))
p1 = Person()
p1.print_info()
p2 = Person('zhangsan',18)
p2.print_info()
p2.get_count()
p3 = Person('lisi',12)
p3.print_info()
Person.get_count()
del p2
Person.get_count()
运行结果:
__init__
name :none
age :0
__init__
name :zhangsan
age :18
count :2
__init__
name :lisi
age :12
count :3
__del__
count :2
__del__
__del__
类方法和实例方法不同,它可以由类名或对象来调用;而实例方法只能由对象调用。在正常的使用中,类方法还是用类名来访问比较好;一般对类变量的操作都会放在类方法中。
6.2.5静态方法
在类的定义中,还有一种方法称为静态方法。静态方法中一般用来存放逻辑性的代码,它与类、对象之间没有任何交互。
静态方法格式:
@staticmethod
def funcname(parameter_list):
pass
其中,staticmethod是装饰器,表示这个方法是静态方法;parameter_list是参数列表,不需要传入self或cls。
#程序6-12
class Person():
#this is a test
count = 0 #类变量
def __init__(self,name = 'none',age = 0):
print('__init__')
self.name = name #实例变量
self.age = age #实例变量
Person.count += 1
def __del__(self):
print('__del__')
Person.count -= 1
def print_info(self):
print('name:'+ self.name,', age:'+ str(self.age),end = ' , ')
Person.chinese_zodiac(self.age)
@classmethod
def get_count(cls): #类方法
print('count :'+ str(cls.count))
@staticmethod
def chinese_zodiac(age): #生肖
a = age%12
if a==0:
print('生肖:猪')
elif a==1:
print('生肖:狗')
elif a==2:
print('生肖:鸡')
elif a==3:
print('生肖:猴')
elif a==4:
print('生肖:羊')
elif a==5:
print('生肖:马')
elif a==6:
print('生肖:蛇')
elif a==7:
print('生肖:龙')
elif a==8:
print('生肖:兔')
elif a==9:
print('生肖:虎')
elif a==10:
print('生肖:牛')
elif a==11:
print('生肖:鼠')
p1 = Person()
p1.print_info()
p2 = Person('zhangsan',18)
p2.print_info()
p3 = Person('lisi',12)
p3.print_info()
p4 = Person('lzb',28)
p4.print_info()
运行结果:
__init__
name:none , age:0 , 生肖:猪
__init__
name:zhangsan , age:18 , 生肖:马
__init__
name:lisi , age:12 , 生肖:猪
__init__
name:lzb , age:28 , 生肖:羊
__del__
__del__
__del__
__del__
代码中,使用静态方法chinese_zodiac来计算生肖,以2019年基本年份,以年龄为参数。
静态方法可以用类名和对象来调用。在正常使用中,一般会由类名来调用;不使用对象调用。