类
我们把重复执行的语句抽象成函数,然后直接调用函数名执行语句,这样代码维护就很方便。但当工程量越来越大,代码就越来越臃肿,维护就比较困难了。相对于面向工程的面向对象,就是以人的思维组织和处理工程项目,我们把一些数据结构和操作数据结构的函数的逻辑整体称为对象,数据结构和函数就是对象的属性和方法。在面向对象的思维里,自然界万物都是对象。当我们把具有相同特征的对象的属性和行为都抽象出来,就可以得到类,具体说,对象是类的实例,是独一无二的,类是对象的抽象,模板。对象是具体的,类是抽象的概念。比如,人是一个概念,有身高,性别等属性,能思考,行动等方法,张三是人这个类的一个实例。
封装
我们把具有相同行为的对象抽象归纳为类,通过封装隐藏内幕实现细节,只向外界暴露简单的接口。当我们创建对象后,只需要知道对象的方法名和需要的参数,就能执行方法中的程序,而不需要知道方法的实现细节。即把属性和方法封装,而只暴露方法名和需要的参数。
class Student(object):
#__init__方法是类的初始化方法,在创建对象时进行初始化操作。
def __init__(self,name=‘张三’,age=15):
self.name = name
self.age = age
def study(self,course):
print('%s正在学习%s' % (self.name,course))
def watch_movie(self):
if self.age>18:
print('%s 只能看动画电影' % self.name)
else:
print('%s 可以看任何电影' % self.name)
def main():
liLei = Student('liLei',22)
liLei.study('machine learning')
liLei.watch_movie()
if __name__=='__main__':
main()
类内的属性和方法可以设置不同的 访问权限,公有的,可以被类外访问的,另一种就是私有的,不能被外界访问的,私有的属性和方法命名时用两个下划线开头。python并没有从语法上保证私有属性和方法的私有性,只是通过双下划线的命名规则妨碍对私有属性的访问。通过_类名__属性和方法名就能继续访问私有属性和方法.这样设定是因为程序员认为开放比封闭好。
class Test(object):
def __init__(self,foo):
#私有属性用双下划线开头
self.__foo = foo
def __bar(self):
print(self.__foo)
print('__bar')
def main():
test = Test('hello')
#直接访问私有属性出错AttributeError: 'Test' object has no attribute '__bar'
test.__bar()
test.__foo
#通过一定的规则可以访问私有属性
test._Test__bar()
test._Test__foo
if '__name__'=='__main__'
main()
在实际开放中并不建议将属性和方法设置成私有,这会导致子类无法访问。大多数程序员通过一种命名惯例,即属性或方法名用单下划线开头,表明本身是受保护的,类外访问徐慎重。这只是一种暗示和惯例。单下划线的属性和方法类外仍可以正常访问。。。
在类外直接访问类的属性和方法是很不规范的,所以可以通过getter(访问器)和setter(修改器)来访问和修改类的属性。因此可以通过@property包装器来包装getter和setter方法,这样对类的属性的访问既安全也方便。
class Person(object):
def __init__(self,name,age):
self._name = name
self._age = age
@property
def age(self):
return self._age
@property
def name(self):
return self._name
@age.setter
def age(self,value):
self._age = value
def play_movie(self):
if self._age<18:
print('年龄小于18,只能看动画电影!')
else:
print('可以看任何电影!')
def main():
person = Person('liWei',15)
print(person._age)
print(person.name)
person.play_movie()
person.age = 28
person.play_movie()
person._is_gay = True
#AttributeError: can't set attribute
#person.name = '牛向林'
if __name__ == '__main__':
main()
python是一种动态语言,可以在程序运行中绑定属性和方法,当然也可以解绑定。如果要限定自定义类型的对象可以绑定的属性,可以在类中通过定义__slots__变量来限定。需要注意的是,__slots__限定绑定的属性只对当前类的对象有效,对子类没有任何作用。
class Person(object):
#限定类对象可以绑定的属性,对子类对象无效
__slots__ = ['_name','_age','_gender']
def __init__(self,name,age):
self._name = name
self._age = age
@property
def age(self)
return self._age
@age.setter
def age(self,value):
self._age = value
def main():
person = Person('LiWei',28)
person._gender = male
#'Person' object has no attribute '_is_gay'
#person.is_gay = True
if __name__ == '__main__':
main()
类中定义的方法有三种,对象方法、静态方法和类方法。对象方法是给对象传递消息,通过创建对象,对象.方法名()调用。类方法是静态方法,无需实例化对象,不再需要self作为第一参数。可以通过类名.方法名()调用。例如定义一个三角形类,需要判断传入的三条边是否能够构成三角形,这个判断方法需要在创建三角形对象前调用(还不知道能不能构成三角形),所以这个方法是静态方法,而不是对象方法。静态方法需要包装器@staticmethod。类方法需要包装器@classmethod,无需实例化对象,可以通过类名.方法名()调用,而且隐含参数不再是self,而是cls,表示类本身。这种用法爬虫框架scrapy有经常用,scrapy提供一个settings.py文件,开发者可以根据自己的实际情况设置一些值。需要获取这些值的类都有一个被classmethod方法修饰的from_settings()函数。这样做的好处是,可以动态实例化一个类,增加代码的灵活性。
class Triangle(object):
def __init__(self,a,b,c):
self._a = a
self._b = b
self._c = c
@staticmethod
def is_triangle(a,b,c):
if a+b>c and a+c>b and b+c>a:
return True
else:
return False
def perimeter(self):
return self._a + self._b + self._c
def area(self):
half_perimeter = self.perimeter()/2
return (half_perimeter*(half_perimeter-self._a)*(half_perimeter-self._b)*(half_perimeter-self._c))
def main():
a, b ,c = 3, 4, 5
if Triangle.is_triangle(a,b,c):
triangle = Triangle(a,b,c)
print('三角形的周长是%.3f' % triangle.perimeter())
print('三角形的面试时%.3f' % triangle.area())
else:
print('三条边不能构成三角形')
if __name__=='__main__':
main()
对象方法,静态方法,类方法
from time import time, localtime
class Time_Test(object):
#self是隐藏参数,代表类实例化对象
def __init__(self,year=2000,month=1,day=1):
self._year = year
self._month = month
self._day = day
#对象方法
def time_show(self):
print('时间是%d年%d月%d日' % (self._year, self._month, self._day))
#静态方法 不需要self隐藏参数,可以直接用类名调用
@staticmethod
def time_valid(year,month,day):
if year<=2019 and month<=12 and day<=31:
return True
else:
return False
#类方法 clas作为隐藏参数,代表类本身,可以直接用类名调用
@classmethod
def time_now(cls):
ctime = localtime(time())
return cls(ctime.tm_year, ctime.tm_mon, ctime.tm_mday)
def main():
year, month, day =2018, 10, 25
#静态方法可以直接调用
if Time_Test.time_valid(year,month,day):
print('时间可能正确')
time_string = Time_Test(year,month,day)
time_string.time_show()
else:
print('时间有点不正确')
#类方法可以直接调用,通过类方法创建对象
time_current = Time_Test.time_now()
time_current.time_show()
if __name__=='__main__':
main()
类之间的关系
is-a关系:继承或泛化关系。比如学生和人,学生和电子产品的关系。子类可以继承父类的属性和方法,除此之外,还可以添加自己都有的属性和方法,所以子类比父类拥有更多的能力,也更特殊,在面向对象编程中可以用子类对象替换父类对象。子类继承父类的方法后,可以重写父类父类已有的方法,从而不同的子类相同的方法有不同的实现方式,叫多态
has-a关系:关联关系。比如部门和员工,汽车和发动机的关系。某个类以成员变量的形式出现在另一个类中,二者是关联关系,比如类A关联与类B,则B体现为A的全局变量,
use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。某个类B以局部变量的形式出现在另一个类A中(类A中方法的参数是B对象,在类A方法中创建了B对象等),二者是依赖关系。
抽象类就是不能被实例化的类,抽象程度高,大部门只定义了方法名,没有方法实现。专门让其他的子类来继承。python中可以通过abc模块的ABCMeta元类和@abstractmethod包装器来达到效果。
class Person(object):
def __init__(self,name,age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@property
def age(self):
return self._age
@age.setter
def age(self,value):
self._age = value
def watch_tv(self,tv):
print('%s 喜欢看 %s' % (self._name,tv))
class Student(Person):
def __init__(self,name,age,grade):
super().__init__(name,age)
self._grade = grade
@property
def grade(self):
return self._grade
@grade.setter
def grade(self,value):
self._grade = value
def study(self,course):
print('%s年级的%s正在学习%s' % (self._grade,self._name,course))
class Teacher(Person):
def __init__(self,name,age,title):
super().__init__(name,age)
self._title = title
@property
def title(self):
return self._title
@title.setter
def title(self,value):
self._title = value
def teach(self,course):
print('%s%s正在学习%s' % (self._title,self._name,course))
def main():
liWei = Student('LiWei',12,'三')
niuXiangLin = Teacher('NiuXiangLin',22,'硕士生')
liWei.study('数据结构')
niuXiangLin.teach('c语言')
if __name__=='__main__':
main()
from abc import ABCMeta,abstractmethod
class Pet(object,metaclass=ABCMeta):
def __init__(self,name):
self._name = name
@property
def name(self):
return self._name
@abstractmethod
def make_voice(self):
pass
class Dog(Pet):
def make_voice(self):
print('%s : 汪汪汪。。' % self._name)
class Cat(Pet):
def make_voice(self):
print('%s: 喵喵喵。。' % self._name)
def main():
pet = [Dog('元宝'), Cat('凯瑟琳'),Dog('阿黄')]
for one in pet:
one.make_voice()
if __name__=='__main__':
main()