知识内容:
1.面向对象编程介绍
2.类与对象
3.面向对象三大特性
4.属性
5.面向对象相关内置函数
一、面向对象编程介绍
1.面向对象与面向过程
编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式,当然也有些语言可以同时支持多种编程范式。常见的编程范式有面向过程、面向对象以及函数式编程
关于函数式编程看这里: http://www.cnblogs.com/wyb666/p/8638799.html
面向对象出现之前,编程世界中一直都是面向过程的,此时结构化程序设计是程序设计的主流。
面向过程是分析出解决问题的步骤,然后用函数一步一步实现这些步骤,使用的时候一个一个一次调用就可以了。而面向对象是把构成问题的事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事务在整个解决方案中的步骤中的行为,例如五子棋:
(1)开始游戏 (2)黑子先走 (3)绘制画面 (4)判断输赢 (5)轮到白子 (6)绘制画面 (7)判断输赢 (8)返回步骤2 (9)输出最后结果
然后把上面的过程用函数实现就解决了实际问题,而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为:
(1)黑白双方,这两方的行为是一模一样 (2)棋盘系统,负责绘制整个系统 (3)规则系统,负责判定诸如犯规、输赢等
第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化棋盘对象接受棋子的输入然后负责在屏幕上显示出这种变化,提示利用规则系统对输赢进行判断可以看出面向对象是根据功能来划分问题,而不是步骤。同样是绘制棋局,在面向过程的设计中需要多个步骤执行该任务,但是这样很可能导致不同步骤的绘制棋局程序不同,因为设计人员会根据实际情况对绘制程序进行简化。而面向对象的设计中,绘图只能在棋盘对象中出现,从而保证了绘图的统一
2.面向对象的优点
面向对象程序设计的出发点之一就是弥补面向过程程序设计过程中的一些缺点: 面向对象中对象是程序的基本元素,它将数据和操作都紧密连接在一起,
(1)数据抽象的概念可以在保持外部接口不变的情况下改变内部实现,从而减少甚至避免对外界的干扰
(2)通过继承可以大幅减少多余的代码,并可以方便地拓展现有代码,提高编程效率,也降低出错概率,降低软件维护的难度
(3)结合面向对象分析、面向对象设计,允许将问题域中的对象直接映射到程序中,减少软件开发过程中的中间环节的转换过程
3.何时使用面向对象编程
面向对象的程序与人类对事物的抽象理解密切相关。举一个例子,虽然我们不知道精灵宝可梦(口袋妖怪)这款游戏的源码,但是可以确定的是它的程序是通过面向对象的思想来编写的,我们将游戏中的每一种精灵看作一个类,而具体的某只精灵就是其中一个类的一个实例对象,所以每种精灵的程序具有一定的独立性。程序员可以同时编写多只精灵的程序,它们之间不会互相影响。为什么我们这里不能使用面向过程编程呢?大家想一想,如果程序员要开发新的精灵,那么就必须对之前的程序做大规模的修改,以使程序的各个函数能正常工作(以前的函数没有新精灵的数据)现在的程序和软件开发都是使用面向对象编程的,最重要的原因还是其良好的抽象性。但对于小型程序和算法来说,面向对象程序一般比面向过程的程序难以设计,所以编程时也要注意掌握两种思想,发挥出它们的长处
面向对象编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容
4.面向对象的核心
Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
Object 对象
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个容器,里面包含着类的数据和方法
Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
5.多种编程范式的比较
- 面向过程:根据业务逻辑从上到下写垒代码
- 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
- 面向对象:对函数进行分类和封装,让开发“更快更好更强...”
二、类与对象
1.类与对象基本概念
类:是具有相同特征的一类事物(人,狗,老虎)
对象/实例:具体的某一事物
实例化:类到对象的过程(实例=类名(参数1,参数2))
2.python类定义与实例化对象
python中使用class来定义类
1 # class是定义类的关键字 2 class Bike: 3 compose = ['frame', 'wheel', 'pedal'] # 类中的成员 4 5 # 实例化对象 6 my_bike = Bike() 7 you_bike = Bike() 8 print(my_bike.compose) # 访问对象中的成员或方法 9 print(you_bike.compose)
3.类的组成部分
(1)类:
变量
实例变量 -> 赋给实例(对象),实例变量又称为静态属性
类变量
方法 -> 又称为动态属性
构造方法(__init__) -> 在类实例化时做一些类的初始化工作
析构函数(__del__)
静态方法与类方法
1 class MyClass: 2 message = "Hello, programmer!" # 类变量 3 4 def show(self): # 类方法 5 print(self.message) 6 print("Here is %s in %s!" % (self.name, self.color)) 7 8 def __init__(self, name="wyb", color="red"): # 构造函数 9 print("Constructor is called with params: ", name, "", color) 10 self.name = name # 定义实例变量 11 self.color = color # 定义实例变量 12 13 def __del__(self): # 析构函数 14 print("Destructor is called for %s!" % self.name) 15 16 17 test = MyClass("David") # 实例化对象 18 test.show() 19 print("Color of test is: ", test.color, " ") 20 21 test2 = MyClass("linda", "Yellow") # 实例化对象 22 test2.show() 23 print("Name of test2 is: ", test2.name)
注: 在python中函数与方法是有区别的!方法一般与特定实例绑定的函数,通过对象调用方法时,对象本身将被作为一个第一个参数传递过去,普通函数不具备这个特点!
(2)实例变量与类变量的区别:
实例变量的作用域就是实例(对象)本身,类变量的作用域是这个类对应的所有对象所共有的,类变量可以通过对象访问也可以直接通过类名来访问
类变量的作用: 所有对象共用,节约开销
1 class Test: 2 n = 123 # 类变量 3 4 def __init__(self, name="wyb", position="student"): 5 # 实例变量(静态属性) 6 self.name = name 7 self.position = position 8 9 10 print(Test.n) 11 c1 = Test("woz", "worker") 12 c2 = Test() 13 print(c1.n, c1.name, c1.position) 14 print(c2.n, c2.name, c2.position)
(3)self参数
self代表类的实例即对象本身,在类的实例方法中都必须有self参数,并且必须是方法的第一个参数,在类的实例方法中访问实例属性时需要通过以self为前缀,但在外部通过对象名调用这些方法时不需要传递这个参数
1 class A: 2 def __init__(self, v): 3 self.n = v 4 5 def show(self): 6 print(self.n) 7 8 9 a = A(3) 10 a.show()
(4)私有属性和公有属性
python中并没有对私有属性提供严格的访问保护机制。在定义类的属性时,如果属性名以两个下划线开头那么这些属性就是私有属性。
私有属性的访问:
- 不能在类的外部直接访问
- 通过调用对象的公有方法访问
而公有属性是可以公开使用的,既可以在类的内部进行访问,也可以在类的外部程序中使用
1 class MyClass(object): 2 def __init__(self, name="unset", color="black"): 3 print("Constructor is called with params: ", name, " ", color) 4 self.__name = name 5 self.__color = color 6 7 def __del__(self): 8 print("Destructor is called for %s" % self.__name) 9 10 11 inst = MyClass("Jojo", "White") 12 # print(inst.__name)是错误的,因为__name是私有属性在类外无法访问!
(5)静态方法与类方法
静态方法和类方法都可以通过类名和对象名调用,但静态方法不能访问类或实例中任何属性,类方法不能直接访问实例属性,只能访问类属性;
另外类方法的第一个参数为cls,并且在调用类方法时不需要为该参数传递值
静态方法: 在函数名上加@staticmethod
类方法: 在函数名上级加@classmethod
1 class MyClass(): 2 message = "Hello, programmer!" 3 4 def show(self): 5 print(self.message) 6 print("Here is %s in %s!" % (self.name, self.color)) 7 8 @staticmethod # 静态方法 9 def printMessage(): 10 print("printMessage is called") 11 print(MyClass.message) 12 13 @classmethod # 类方法 14 def createObj(cls, name, color): 15 print("Obj will be created: %s(%s, %s)" % (cls.__name__, name, color)) 16 return cls(name, color) 17 18 def __init__(self, name="wyb", color="red"): 19 print("Constructor is called with params: ", name, "", color) 20 self.name = name 21 self.color = color 22 23 def __del__(self): 24 print("Destructor is called for %s!" % self.name) 25 26 27 MyClass.printMessage() # 调用静态方法 28 29 inst = MyClass.createObj("Toby", "green") # 调用类方法 30 print(inst.message)
三、面向对象三大特性
1.封装
封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏
python中的私有属性就是为了数据封装而设置的属性,一般只能在类的成员方法中来访问,这样就使这些数据对外界来说是隐藏的
2.继承
面向对象编程语言的一个主要功能就是“继承”。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
(1)继承- 继承是一种创建新类的方式
- 新建的类可以创建一个或多个父类,父类有称为基类或者超类
- 新建的类称为派生类或者子类
- 在python中类的继承分为:单继承或多继承
1 class People(object): 2 def __init__(self, name, age): 3 self.name = name 4 self.age = age 5 6 def eat(self): 7 print("%s is eating..." % self.name) 8 9 def talk(self): 10 print("%s is talking..." % self.name) 11 12 def sleep(self): 13 print("%s is sleeping..." % self.name) 14 15 16 class Man(People): 17 def __init__(self, name, age, money): 18 # People.__init__(self,name,age) 19 super(Man, self).__init__(name, age) # 新式类写法 20 self.money = money 21 print("%s 一出生就有%s money" % (self.name, self.money)) 22 23 def piao(self): 24 print("%s is piaoing ..... 20s....done." % self.name) 25 26 def sleep(self): 27 People.sleep(self) 28 29 30 class Woman(People): 31 def get_birth(self): 32 print("%s is born a baby...." % self.name) 33 34 35 m1 = Man("NiuHanYang", 22, 30000) 36 m1.eat() 37 m1.piao() 38 m1.sleep() 39 m1.talk() 40 41 w1 = Woman("shanshan", 20) 42 w1.eat() 43 w1.sleep() 44 w1.talk() 45 w1.get_birth()
1 # class People: 经典类 2 class People(object): #新式类 3 def __init__(self,name,age): 4 self.name = name 5 self.age = age 6 self.friends = [] 7 print("--doens't run ") 8 def eat(self): 9 print("%s is eating..." % self.name) 10 def talk(self): 11 print("%s is talking..." % self.name) 12 def sleep(self): 13 print("%s is sleeping..." % self.name) 14 15 class Relation(object): 16 def __init__(self,n1,n2): 17 print("init in relation") 18 def make_friends(self,obj): #w1 19 print("%s is making friends with %s" % (self.name,obj.name)) 20 self.friends.append(obj.name) 21 class Man(Relation,People): 22 # def __init__(self,name,age,money): 23 # #People.__init__(self,name,age) # 经典类写法 24 # super(Man,self).__init__(name,age) # 新式类写法 25 # self.money = money 26 # print("%s 一出生就有%s money" %(self.name,self.money)) 27 def piao(self): 28 print("%s is piaoing ..... 20s....done." % self.name) 29 def sleep(self): 30 # People.sleep(self) # 经典类写法 31 super(Man,self).sleep(self) # 新式类写法 32 print("man is sleeping ") 33 class Woman(People,Relation): 34 def get_birth(self): 35 print("%s is born a baby...." % self.name) 36 37 m1 = Man("NiuHanYang",22)
py2 经典类是按深度优先来继承,新式类是按广度优先来继承,而py3不论哪种类都是按广度优先继承
多继承:
1 class BaseA(object): 2 def move(self): 3 print("move called in BaseA") 4 5 6 class BaseB(object): 7 def move(self): 8 print("move called in BaseB") 9 10 11 class BaseC(BaseA): 12 def move(self): 13 print("move called in BaseC") 14 15 16 class Sub(BaseC, BaseB): 17 pass 18 19 20 inst = Sub() 21 inst.move()
上述代码输出结果: move called in BaseC
组合指的是,在一个类中以另外一个类的对象(也就是实例)作为数据属性,称为类的组合;也就是说:一个类的属性是另一个类的对象,就是组合
1 # 龟类 2 class Turtle(object): 3 def __init__(self, x): 4 self.num = x 5 6 7 # 鱼类 8 class Fish(object): 9 def __init__(self, x): 10 self.num = x 11 12 13 # 水池类 14 class Pool(object): 15 def __init__(self, x, y): 16 # 把类的实例化放在一个新类里面 17 self.turtle = Turtle(x) 18 self.fish = Fish(y) 19 20 def output_num(self): 21 print("水池里总共有龟%d只,鱼%d条!" % (self.turtle.num, self.fish.num)) 22 23 24 pool = Pool(3, 7) 25 pool.output_num()
3.多态
1 class People(object): 2 def __init__(self, name, age): 3 self.name = name 4 self.age = age 5 6 def skills(self): 7 pass 8 9 10 class Man(People): 11 def skills(self): 12 print("%s的技能是工作" % self.name) 13 14 15 class Woman(People): 16 def skills(self): 17 print("%s的技能是生孩子" % self.name) 18 19 20 p1 = Man("wyb", 21) 21 p2 = Woman("cy", 20) 22 p1.skills() 23 p2.skills()
多态: 接口的重用,一种接口多种实现
多态实现接口:
1 class Animal: 2 def __init__(self, name): 3 self.name = name 4 5 def talk(self): # Abstract method, defined by convention only 6 pass 7 8 @staticmethod 9 def animal_talk(obj): 10 obj.talk() 11 12 class Cat(Animal): 13 def talk(self): 14 print('Meow!') 15 16 17 class Dog(Animal): 18 def talk(self): 19 print('Woof! Woof!') 20 21 22 d = Dog("dog") 23 c = Cat("cat") 24 25 Animal.animal_talk(c) 26 Animal.animal_talk(d)
四、属性
1.属性的基本使用
1 class Test(object): 2 def __init__(self, value): 3 self.__value = value 4 5 @property 6 def value(self): # 只读,无法修改和删除 7 return self.__value 8 9 10 t = Test(3) 11 print(t.value) 12 13 # t.value = 5 # 只读属性,不允许修改!
由属性的定义和调用要注意一下几点:
- 定义时,在普通方法的基础上添加 @property 装饰器;
- 定义时,属性仅有一个self参数
- 调用时,无需括号
方法:obj.func()
属性:obj.prop
注意:
属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象
属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能
2.属性的两种定义方式
属性的定义有两种方式:
- 装饰器 即:在方法上应用装饰器
- 静态字段 即:在类中定义值为property对象的静态字段
属性具体看这里: http://www.cnblogs.com/wupeiqi/p/4766801.html
(1)装饰器
Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富
- 经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
- 新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法
经典类,只具有一种@property装饰器
# ############### 定义 ###############
class Goods:
@property
def price(self):
return "666"
# ############### 调用 ###############
obj = Goods()
result = obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
新式类,具有三种@property装饰器
# ############### 定义 ###############
class Goods(object):
@property
def price(self):
print '@property'
@price.setter
def price(self, value):
print '@price.setter'
@price.deleter
def price(self):
print '@price.deleter'
# ############### 调用 ###############
obj = Goods()
obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
obj.price = 666 # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数
del obj.price # 自动执行 @price.deleter 修饰的 price 方法
(2)静态字段
1 # 可读可修改但是不可删除属性 2 class Test(object): 3 def __init__(self, value): 4 self.__value = value 5 6 def __get(self): 7 return self.__value 8 9 def __set(self, v): 10 self.__value = v 11 12 value = property(__get, __set) 13 14 def show(self): 15 print(self.__value) 16 17 18 t = Test(3) 19 print(t.value) 20 t.value = 5 21 print(t.value) 22 23 # del t.value # 不可删除属性
五、面向对象相关内置函数
1.object:创建一个新的object对象
官方解释:
class object
Return a new featureless object. object is a base for
all classes. It has the methods that are common to all instances of
Python classes. This function does not accept any arguments.
Note:object does not have a __dict__, so you can’t assign arbitrary attributes to an instance of the object class.
总结:
- object类是Python中所有类的基类,如果定义一个类时没有指定继承哪个类,则默认继承object类
- object类定义了所有类的一些公共方法
- object类没有定义 __dict__,所以不能对object类实例对象尝试设置属性值
2.super:根据传入的参数创建一个新的子类和父类关系的代理对象
1 class Foo(object): 2 def __init__(self, name): 3 self.name = name 4 5 6 class Bar(Foo): 7 def __init__(self, name): 8 super(Bar, self).__init__(name) # 调用父类的构造函数 9 10 def show_name(self): 11 print(self.name) 12 13 14 c = Bar("wyb") 15 c.show_name()
3. isinstance: 判断一个对象是否是另一个类的对象
1 class Foo(object): 2 pass 3 4 obj = Foo() 5 6 print(isinstance(obj, Foo)) 7 print(isinstance(3, int)) 8 print(isinstance(3.2, str))
4.issubclass:判断类是否是另外一个类或者类型元组中任意类元素的子类
1 class Foo(object): 2 pass 3 4 class Bar(Foo): 5 pass 6 7 issubclass(Bar, Foo)
5.装饰器相关
- property:标示属性的装饰器
- classmethod:标示方法为类方法的装饰器
- staticmethod:标示方法为静态方法的装饰器