本篇博客主要写类、面向对象定义,三大特性:封装、继承、多态,也会简单的介绍魔法(后面将写一篇博文概述)。
面向对象
目前面向对象还没有统一的定义,面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。简单一点来概述,面向对象就是使用对象进行程序设计。
(1)面向对象与面向过程的区别
- 面向过程,是根据业务逻辑所需要的步骤,使用函数将这些步骤一步一步实现,使用的时候一个一个依次调用即可。
- 面向对象,将业务逻辑抽象成多个对象,然而在对象中用属性和行为(方法)的定义来描述业务逻辑。
(2)面向对象和面向过程的优缺点
- 面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
- 面向对象
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
(3)面向对象最重要的两个基本概念
类:类由属性(一些特征数据)和方法(行为)组成,拥有相同或者相近属性、行为的事物可以抽象成为一个类。类往往是抽象的、没有实体。
对象:根据所属类
模板创造出来的实实在在的事物。在程序中我将这个实实在在的事物称之为实例,我们为它的属性赋上特定的值。
#定义类 class Student(object): #初始化属性值 def __init__(self,name,stuId,age): self.name = name self.stuId = stuId self.age = age #print输出对象时被调用 def __str__(self): introduce = "我是: " + self.name + ",学号: " + str(self.stuId) + ",年龄: " + str(self.age) + "岁" return introduce def study(self): print("我会学习") #创建对象 Ming = Student("小明",123545,18) print(Ming) Ming.study() 输出: 我是: 小明,学号: 123545,年龄: 18岁 我会学习
注:定义类对象时一定要传入一个参数self可以为其他字符,便于可读性约定俗成为self,self相当于Java、C++中的this。由于类方法与对象实例相关,Python语言在定义类方法时必须传入一个self,否则报错:study() takes 0 positional arguments but 1 was given。
封装
当我们要修改对象属性时,通常有两种方法:
- 对象名.属性名 = 数据 (直接修改)
- 对象名.方法名() (间接修改)
由于直接修改的修改可能导致属性数据不合理,为了更好的保存属性安全性,即不可随意修改,我们有如下的处理方式:
- 定义私有属性。不像其他语言有private 、public等关键字修饰,Python直接用双下划线“__”表示私有属性,可以修饰属性和方法。
- 对外提供访问的接口。通常使用getXXX()、setXXX()实现,不过Python中还可以使用property属性来实现。
class Money(object): def __init__(self,money): self.__money = money def getMoney(self): return self.__money def setMoney(self,value): if isinstance(value,int): #内建函数判断输入类型 self.__money = value else: print("error:不是整数,%s"%type(value)) #通过property提供外部访问 money = property(getMoney,setMoney) ming = Money(100) print("-1-",ming.getMoney()) ming.setMoney(200) print("-2-",ming.getMoney()) print("="*30) print("property获取属性值:money=%d"%ming.money) ming.money = 555 print("property修改属性值:money=%d"%ming.money) 输出: -1- 100 -2- 200 ============================== property获取属性值:money=200 property修改属性值:money=555
启动python解释器,输入dir(__builtins__), 可以看到很多python解释器启动后默认加载的属性和函数,这些函数称之为内建函数, 这些函数因为在编程时使用较多,cpython解释器用c语言实现了这些函数,启动解释器时默认加载,可以直接调用。
继承
Python语言支持多继承,在定义类时小括号()里面的即为父类名字,子类可以继承父类的属性、方法,但是私有属性、方法都是不对外公开的,所以不可被继承和访问。
class Animal(object): def __init__(self,name): self.name = name def eat(self): print("Animal----eat()") def run(self): print("Animal----不能飞,只能奔跑。。。") class Cat(Animal): def eat(self): print("cat----重写父类----eat()") def sleep(self): print("cat----我的爱好是睡觉") miao = Cat("哆啦A梦") print("我是:%s"%miao.name) miao.eat() miao.run() miao.sleep() 输出: 我是:哆啦A梦 cat----重写父类----eat() Animal----不能飞,只能奔跑。。。 cat----我的爱好是睡觉
多态
不同子对象调用相同的父类方法,在运行时产生不同的结果。多态以继承和重写父类方法为前提,增加了代码的灵活性,是调用方法的技巧,不会影响到类的内部设计。
class Dog(object): def __init__(self,name): self.name = name def game(self): print("%s 很开心的玩耍。。。"%self.name) class XiaoTianQuan(Dog): def game(self): print("%s 飞到天上玩..."%self.name) class Person(object): def __init__(self,name): self.name = name def gameWithDog(self,dog): print("%s 和 %s玩的停不下来"%(self.name,dog.name)) dog.game() xiaoHuaGou = Dog("小花狗") Jack = Person("杰克") Jack.gameWithDog(xiaoHuaGou) xtq = XiaoTianQuan("哮天犬") Jack.gameWithDog(xtq) 输出: 杰克 和 小花狗玩的停不下来 小花狗 很开心的玩耍。。。 杰克 和 哮天犬玩的停不下来 哮天犬 飞到天上玩...
类属性、实例属性
前面几个类在__init__中的属性就是实例属性,Python中所谓的类属性相当于java、C++中的类静态成员变量,即类属性就是类对象所拥有的属性,它被所有类对象的实例对象共有,在内存中只存在一个副本。对于公有类属性在类外可以通过类对象和实例对象访问,私有就无法访问了。
class Person(object): address = "上海" #类属性 def __init__(self,name,age): self.name = name #实例属性 self.age = age person = Person("华仔","18") print("实例属性:",person.name,person.age) print("类属性:",Person.address) #下面报错:AttributeError: type object 'Person' has no attribute 'name' #print(Person.name) print("通过实例对象修改类属性address:") print("---修改前---") print("类属性:",Person.address) print("实例属性:",person.address) person.address = "武汉" print("---修改后---") print("类属性:",Person.address) print("实例属性:",person.address) #实例属性会屏蔽同名类属性 del person.address #删除实例对象属性 print("---实例属性:",person.address) 输出: 实例属性: 华仔 18 类属性: 上海 通过实例对象修改类属性address: ---修改前--- 类属性: 上海 实例属性: 上海 ---修改后--- 类属性: 上海 实例属性: 武汉 ---实例属性: 上海
如果需要在类外去修改类属性,必须通过类对象引用去修改;如果使用实例对象引用去修改,实际上产生的是一个同名的实例属性,这种方式修改的是实例属性,并不会改变类属性的值(这一点可以类似理解为全局变量和局部变量)。之后如果通过实例对象去引用该名称的实例属性,实例属性将屏蔽类属性,即此时引用的是实例属性,除非删除这个实例属性。
类方法、静态方法
- 类方法是类对象拥有的方法,需要用修饰器@classmethod标识的方法,对于类方法第一参数必须是类对象,一般使用cls作为第一个参数(可以用其他字符,约定俗成用cls),可以使用实例对象和类对象引用去访问。类方法有一个重要作用是可以修改类属性。
class People(object): city = "北京" @classmethod def getCity(cls): return cls.city @classmethod def setCity(cls,city): cls.city = city people = People() #可以通过实例对象引用类方法 print(people.getCity()) #通过类对象引用类方法 print(People.getCity()) #修改类属性 people.setCity("上海") print(people.getCity()) print(People.getCity()) 输出: 北京 北京 上海 上海
当类方法修改类属性之后,实例对象、类对象访问的类属性都将发生变化。
- 静态方法,使用修饰器@staticmethod来修饰,静态方法没有必须参数;类方法第一参数必须是cls、通过cls引用的一定是类对象的属性和方法;实例方法第一参数是self,通过self引用的可能是类属性、也可能实例属性,当self引用出现类属性和实例属性名称一样时,实例属性高于类属性。静态方法如果要引用类属性,必须同过类对象来引用。
class People(object): country = "china" @staticmethod def getCountry(): return People.country print(People.getCountry()) people = People() print(people.getCountry()) 输出: china china