简单来说,类是对象的蓝图和模板,而对象是类的实例。类是抽象的概念,而对象是具体的东西。比如人类就属于类,当具体到某一个人的时候这个人就是对象。
在面向对象的编程世界中,一切皆对象,对象都有属性和行为,每个对象都是第一无二的,而且对象一定是属于某个类。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来以后,就可以定义出一个叫做“类”的东西。
在Python中可以用class关键字来定义类,然后再类中通过函数来定义方法,这样就可以将对象的动态特征描述出来。
class Student(object):
'''
__init__是一个特殊方法,用于在创建对象时进行初始化操作
通过这个方法我们可以为学生对象绑定name和age两个属性
'''
def __init__(self,name,age):
self.name = name
self.age = age
def study(self,course_name):
print('%s正在学习%s.' % (self.name,course_name))
def watch_movie(self):
if self.age < 18:
print('%s只能观看《熊出没》.' % self.name)
else:
print('%s正在观看岛国爱情大电影.' % self.name)
我们定义好一个类以后,就可以通过一下方式来创建对象并给对象发消息。
def main():
# 创建学生对象并指定姓名和年龄
stu1 = Student('骆昊', 38)
# 给对象发study消息
stu1.study('Python程序设计')
# 给对象发watch_av消息
stu1.watch_movie()
stu2 = Student('王大锤', 15)
stu2.study('思想品德')
stu2.watch_movie()
在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头,但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来“妨碍”对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们。
如果直接将属性暴露给外界时,我们没有办法检查赋给属性的值是否有效。
例如:
class Student(object):
pass
s = Student()
s.score=90
print(s.score)
s.score=200
print(s.score)
如上所示,我们用一个变量来表示一个学生的单科成绩时,成绩本不应该超过100分,但是通过实例对象我们却可以随意修改score的值,哪怕它超过了100分,我们依然可以将这个成绩打印出来,这是不合理的。
如果我们想要控制这个成绩的值的传入,可以在类Student的内部通过增加俩个函数,set_xxx和get_xxx分别来控制属性score的值的输入和返回,因此我们修改上述代码为:
class Student(object):
def set_score(self,value):
if value>=0 and value <= 100:
self.__score = value #还记得__score吗?前面加一个双下划线,表示private私有属性
else:
raise ValueError('score must between 0 ~ 100!')
def get_score(self):
return self.__score
s = Student()
s.set_score(90)
print(s.get_score())
s.set_score(188)
print(s.get_score())
这样一来,我们就可以到达我们想要的效果,在录入成绩的时候,可以对我们的成绩score的值进行检查判断,从而保证数据的合法性,当然,Python是追求精简的,哪怕少写一对括号,我们的Python也要实现它,这就不得不说我们今天讲的@property了,用了@property后代码如下
class Student(object):
@property
def score(self):
return self.__score
@score.setter
def score(self,value):
if value >= 0 and value <= 100:
self.__score = value
else:
raise ValueError('score must between 0 ~ 100!')
s = Student()
s.score=90
print(s.score)
s.score=238
print(s.score)
如上所示,当set成绩等于90时,符合类内部定义的函数set_score的条件,当set成绩等于238时,会输出返回错误:score must between 0 ~ 100!
这样就达到了我们想要的效果,我们可以检查赋给属性的值是否有效,从而保证数据的合法性,也没有将属性直接暴露给外界。
那么,@property是什么,有什么作用呢?
class Student(object):
@property
def score(self):
return self.__score
@score.setter
def score(self,value):
if value >= 0 and value <= 100:
self.__score = value
else:
raise ValueError('score must between 0 ~ 100!')
s = Student()
s.set_score(90)
print(s.set_score())
s.set_score(238)
print(s.set_score())
@property修饰函数score(getter),将score函数编程score属性输出,此外,@property本身又创建了另一个装饰器@score.setter,负责把一个setter函数编程属性赋值,于是,我们虽然看到了类Student内部定义了俩个函数score,但是他们却呗不同的装饰器装饰,getter函数被@property修饰,setter函数被@property创建的函数setter装饰器@score.setter修饰,因此,我们可以直接用s.score=90来代替之前的s.set_socre(90),达到给score属性赋值的效果.
当然,我们上面创建了@property另一个装饰器函数@xxx.setter,对私有属性_score进行输入值的判断,如果我们不创建装饰器@xxx.setter可以吗?
可以,但是如果不创建@xxx.setter装饰器的话,属性xxx就是一个只读属性,意味着不能修改
例如:我们定义一个函数age函数,并且给它增加一个@property装饰器,但是不创建@xxx.setter装饰器
class Student(object):
@property
def age(self):
self.__age = 26
return self.__age
s = Student()
print(s.age)
'''此时输出为:26'''
我们上面说了,没有添加@xxx.setter装饰器的时候,age只是一个只读属性,如果我们试图修改age的值,是不可行的。
class Student(object):
@property
def age(self):
self.__age = 26
return self.__age
s = Student()
s.age = 39
print(s.age)
'''输出can't set attribute'''
如果我们想要让age既可以读也可以修改,我们可以这样做:
class Student(object):
@property
def age(self):
self.__age = 26
return self.__age
@age.setter
def age(self,value):
self.__age = value
s = Student()
s.age = 39
print(s.age)
此时,age的值就变为既可以读,也可以修改的了
Python是一门动态语言,可以在运行过程中给对象绑定新的属性或方法,当然也可以对已经绑定的属性和方法进行绑定。如果我们需要限定自定义类型的对象只能绑定某些属性时,就需要用到__slots__这个“魔法”了。
注意:__slots__对自定义类型的对象的属性限定只对当前类的对象生效,对子类并不其任何作用。
class Student(object):
'''限定Student对象只能绑定_name,_age属性'''
__slots__ = ('name','_age')
def __int__(self,name,age):
self._age = age
self._name = name
@property
def name(self):
return self.__name
def age(self):
return self._age
如上所示,当Student通过__slots__限制属性只能为name和age后,Student实例不能再绑定其他属性。
在实际应用中,我们写在类中的方法并不需要都是对象方法,例如我们定义一个“三角形”类,通过传入三条边长来构造三角形,并提供计算周长和面积的方法,但是传入的三条边长不一定能构造成一个三角形,因此我们可以先写一个方法来验证三条边长是否可以构成三角形,这就是静态方法。
from math import sqrt
class Triangle(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
@staticmethod
def is_valid(a, b, c):
return a + b > c and b + c > a and a + c > b
def perimeter(self):
return self._a + self._b + self._c
def area(self):
half = self.perimeter() / 2
return sqrt(half * (half - self._a) * (half - self._b) * (half - self._c))
def main():
a, b, c = 3, 4, 5
# 静态方法和类方法都是通过给类发消息来调用的
if Triangle.is_valid(a, b, c):
t = Triangle(a, b, c)
print(t.perimeter())
# 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数
# print(Triangle.perimeter(t))
print(t.area())
# print(Triangle.area(t))
else:
print('无法构成三角形.')
if __name__ == '__main__':
main()
和静态方法类似,在Python中还可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是个对象),通过这个参数我们可以获取和类相关的信息并创建出类的对象。
from time import time, localtime, sleep
class Clock(object):
"""数字时钟"""
def __init__(self, hour=0, minute=0, second=0):
self._hour = hour
self._minute = minute
self._second = second
'''定义类方法'''
@classmethod
def now(cls):
ctime = localtime(time())
return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)
def run(self):
"""走字"""
self._second += 1
if self._second == 60:
self._second = 0
self._minute += 1
if self._minute == 60:
self._minute = 0
self._hour += 1
if self._hour == 24:
self._hour = 0
def show(self):
"""显示时间"""
return '%02d:%02d:%02d' % \
(self._hour, self._minute, self._second)
def main():
# 通过类方法创建对象并获取系统时间
clock = Clock.now()
while True:
print(clock.show())
sleep(1)
clock.run()
if __name__ == '__main__':
main()