文章目录(Table of Contents)
了解面向对象的编程
讲之前推荐一篇文章,面向对象圣经。可以把这一篇学完之后再回过去看一下,讲得还是很好的。下面简单讲一下面向对象编程的思想(这里的知识点也是常看常新)。
以面向过程思想设计程序时,程序是一条条指令的顺序执行,当指令变得多起来时,它们被分隔成我们先前实验中讲解过的函数。
而面向对象思想则是对象视为程序的组成单元,程序的执行通过调用对象提供的接口完成。面向对象的四个核心概念:抽象、封装、继承、多态。
下面讲一下这四个概念在 Python 中的应用。不完全按照这四个核心概念来讲。
参考资料
抽象,封装
我们先看一个简单的类的例子
#创建类
class Foo(object):
def __init__(self,name):
self.name = name
def Bar(self):
print('Bar')
def Hello(self):
print('i am %s' %self.name)
# 根据类Foo创建对象obj
obj = Foo('Dog')
obj.Bar() #执行Bar方法
>> 'Bar'
obj.Hello() #执行Hello方法
>> 'i am Dog'
print(dir(obj))
>> #这个可以看这个对象的方法
上面这个类中,object
是Python中所有对象的祖先,它是所有类的基类。
类需要一个初始化方法__init__
是Python的初始化方法,注意前后各有两个下划线 _,self指代当前的对象。所以在类的实例化的时候,需要Foo('Dog'),之后在类中就是要self来间接调用被封装的内容。
为了看出类有什么用处,我们来看一个练习:游戏人生程序
1、创建三个游戏人物,分别是:
苍井井,女,18,初始战斗力1000
东尼木木,男,20,初始战斗力1800
波多多,女,19,初始战斗力2500
2、游戏场景,分别:
草丛战斗,消耗200战斗力
自我修炼,增长100战斗力
多人游戏,消耗500战斗力
可以看到这个例子中需要创建一个类,初始化的时候需要名字,性别,年龄和战斗力。然后这个类里面有三个方法,分别是草丛战斗,自我修炼和多人游戏,可以参考下面的代码。
class practice_02(object):
def __init__(self,name,gender,age,combat_effectiveness):
self.name = name
self.gender = gender
self.age = age
self.combat_effectiveness = combat_effectiveness
@property
def print_message(self):
print('%s,%s,%s,战斗力为%s' %(self.name,self.gender,self.age,self.combat_effectiveness))
def grass_battle(self):
self.combat_effectiveness = self.combat_effectiveness-200
def self_practice(self):
self.combat_effectiveness = self.combat_effectiveness+100
def multiplayer_game(self):
self.combat_effectiveness = self.combat_effectiveness-500
player = practice_02('仓井井','女','18',1000)
player.print_message
player.grass_battle()#参加一次草丛战斗
player.print_message
player.self_practice()#参加一次自我修炼
player.print_message
player.multiplayer_game()#参加一次多人游戏
player.print_message
继承
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。
例如:
-
猫可以:喵喵叫、吃、喝、拉、撒
-
狗可以:汪汪叫、吃、喝、拉、撒
如果我们要分别为猫和狗创建一个类,那么就需要为猫
和狗
实现他们所有的功能,如下所示:
class 猫:
def 喵喵叫(self):
print '喵喵叫'
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
class 狗:
def 汪汪叫(self):
print '喵喵叫'
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。所以有了继承的概念,我们从猫和狗身上提取共同的特点,来进行编写,如下所示:
class Animal(object):
def eat(self):
print('%s 吃' %self.name)
def drink(self):
print('%s 喝' %self.name)
def shit(self):
print('%s 拉' %self.name)
def pee(self):
print('%s 撒' %self.name)
class Cat(Animal):
def __init__(self,name):
self.name = name
def cry(self):
print('喵喵叫')
cat = Cat('小猫猫')
cat.eat()
cat.cry()
cat.pee()
所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
静态变量
静态变量是可以直接从类访问的,不需要实例化对象就可以访问。还是以上面动物的例子为例,假如说那些动物都来自动物园('zoo'),我们可以在Animal中加一个静态变量,一般声明在__init__
前面。
class Animal(object):
owner = 'zoo'
def eat(self):
print('%s 吃' %self.name)
def drink(self):
print('%s 喝' %self.name)
def shit(self):
print('%s 拉' %self.name)
def pee(self):
print('%s 撒' %self.name)
class Cat(Animal):
def __init__(self,name):
self.name = name
def cry(self):
print('喵喵叫')
cat = Cat('小猫猫')
print(cat.owner)
>> 'zoo'
print(Animal.owner) #不需要实例化,可以直接访问
>> 'zoo'
类方法
类方法和静态变量类似,它也可以通过类名直接访问,类方法用@classmethd
装饰,类方法中可以访问类的静态变量。
class Animal(object):
owner = 'zoo'
def owner_name(cls):
return cls.owner
def eat(self):
print('%s 吃' %self.name)
def drink(self):
print('%s 喝' %self.name)
def shit(self):
print('%s 拉' %self.name)
def pee(self):
print('%s 撒' %self.name)
class Cat(Animal):
def __init__(self,name):
self.name = name
def cry(self):
print('喵喵叫')
cat = Cat('小猫猫')
print(cat.owner_name())
>> 'zoo'
print(Animal.owner_name(Animal))
>> 'zoo'
注意类方法的第一个参数传入的是类对象,而不是实例对象,所以是 cls
。
我们再来看一个例子
class Foo(object):
def __init__(self,name):
self.name = name
def ord_func(self):
#定义普通方法
print('普通方法')
@classmethod
def class_func(cls):
#定义类方法
print('类方法')
@staticmethod
def static_func():
#定义静态方法
print('静态方法')
f = Foo('江苏')
f.ord_func()
>> '普通方法'
#调用类方法
Foo.class_func()
>> '类方法'
#调用静态方法
Foo.static_func()
>> '静态方法'
property
在Python中,property
可以将方法变成一个属性来使用,借助property
可以实行Python风格的getter/setter
,即可以通过property
获得和修改对象的某一个属性。
我们就看下面的例子里理解吧。
class goods(object):
def __init__(self):
#原价
self.original_price = 100
#折扣
self.discount = 0.8
@property
def price(self):
#实际价格=原价*折扣
new_price = self.original_price*self.discount
return new_price
@price.setter
def price(self,value):
self.original_price = value
@price.deleter
def price(self,value):
del self.original_price
obj = goods()
print(obj.price) #查看商品的价格
>> 80
obj.price = 200 #设置商品的价格
print(obj.price) #查看现在商品的价格
>> 160
#del obj.price 删除商品价格
初始化函数__init__
当出现继承的情况的时候, 一定要注意初始化函数的使用. 第一种情况是, 子类没有定义初始化函数, 父类的初始化函数会被调用.
在下面的例子中, 我们在父类中定义了初始化函数, 但是在子类Child中没有定义. 于是在初始化子类的时候, 我们会调用父类的初始化函数, 所以在子类中需要传入参数.
- #定义父类:Parent
- class Parent(object):
- def __init__(self, name):
- self.name = name
- print("create an instance of:", self.__class__.__name__)
- print("name attribute is:", self.name)
- #定义子类Child ,继承父类Parent
- class Child(Parent):
- pass
- c = Child(name="init Child")
- print(c.name)
- """
- create an instance of: Child
- name attribute is: init Child
- init Child
- """
第二种情况是, 子类定义了自己的初始化函数, 而在子类中没有显示调用父类的初始化函数, 则父类的属性不会被初始化. 在下面代码中, 因为我们在子类中定义了初始化函数, 所以不会调用父类中的初始化函数, 所以没有name属性.,
- #定义父类:Parent
- class Parent(object):
- def __init__(self, name):
- self.name = name
- print("create an instance of:", self.__class__.__name__)
- print("name attribute is:", self.name)
- #定义子类Child ,继承父类Parent
- class Child(Parent):
- #子类中没有显示调用父类的初始化函数
- def __init__(self):
- print("call __init__ from Child class")
- c = Child()
- """
- all __init__ from Child class
- """
- print(c.name) # 此时会报错
第三种情况是, 如果子类定义了自己的初始化函数, 显示调用父类, 子类和父类的属性都会被初始化.
- class Parent(object):
- def __init__(self, name):
- self.name = name
- class Child(Parent):
- def __init__(self):
- super(Child,self).__init__(name='123') #要将子类Child和self传递进去
- c = Child()
- print(c.name)
- """
- 123
- """
上面我们通过super的方式来传入参数.
Super的详细说明
super主要来调用父类方法. 在子类中, 一般会定义与父类相同的属性(数据属性, 方法), 从而来实现子类特有的行为.
例如下面子类重写了父类的函数, 但是又同时想调用父类的函数, 这个时候需要通过父类名来进行调用. 需要将self显式的传递进去.
- class Parent(object):
- Value = "Hi, Parent value"
- def fun(self):
- print("This is from Parent")
- class Child(Parent):
- Value = "Hi, Child value"
- def fun(self):
- print("This is from Child")
- Parent.fun(self) #调用父类Parent的fun函数方法
- c = Child()
- c.fun()
- """
- This is from Child
- This is from Parent
- """
这种方式有一个不好的地方就是, 需要经父类名硬编码到子类中, 为了解决这个问题, 可以使用Python中的super关键字:
- class Parent(object):
- Value = "Hi, Parent value"
- def fun(self):
- print("This is from Parent")
- class Child(Parent):
- Value = "Hi, Child value"
- def fun(self):
- print("This is from Child")
- #Parent.fun(self)
- super(Child,self).fun() #相当于用super的方法与上一调用父类的语句置换
- c = Child()
- c.fun()
- """
- This is from Child
- This is from Parent
- """
我们再看一下与初始化函数结合进行使用的例子. 我们使用super之后调用父类中的初始化函数, 此时value的值是父类的值. 接着我们重新赋值, 打印的值就是子类的值.
- class Parent(object):
- def __init__(self):
- self.value = "Hi, Parent value"
- class Child(Parent):
- def __init__(self):
- super(Child, self).__init__()
- print(self.value) # 此时是父类中自定义的值
- self.value = "Hi, Child value"
- print(self.value) # 此时是子类中的值
- c = Child()
- """
- Hi, Parent value
- Hi, Child value
- """
__call__方法
关于call的详细说明, 可以查看链接(下面是一些我的简单理解): Python call详解
关于__call__
方法, 不得不先提到一个概念, 就是可调用对象(callable). 我们平时自定义的函数, 内置函数和类都属于可调用对象.
但凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象, 判断对象是否为可调用对象可以用函数callable.
call方法可以把类实例当做函数调用。__call__在那些类的实例经常改变状态的时候会非常有效。我们可以看下面的一个例子. 使用call去改变实例里面的参数.
- class X(object):
- def __init__(self, a, b):
- self.a = a
- self.b = b
- def __call__(self, a, b):
- print('call')
- self.a = a
- self.b = b
在实例初始化的时候, 会初始化a和b. 接着我们可以像使用函数一样修改a和b的值.
- x = X(2,3)
- print(x.a, x.b)
- # >> 2 3
- x(4,5) # 像函数一样调用
- print(x.a, x.b)
- # >> call
- # >> 4 5
类的特殊成员__doc__
这里我们讲一个类的特殊成员,__doc__
,这个是输出类的描述信息,什么意思呢,可以看下面的例子。
class describe(object):
"""可以显示类的说明信息
"""
def __init__(self,name):
self.name = name
def speak_name(self):
return self.name
des = describe('dog')
des.__doc__
>> '可以显示类的说明信息\n'
所以说我们可以使用__doc__
来看一下类的说明的文档。
类的特殊成员__repr__
repr
是用来指定输出的格式的。
我们可以看一下下面的这个例子,如果直接打印类,打印出来的是地址,这个时候我们可以使用repr
来定义输出的格式。
- In [1]: class Test(object):
- ...: def __init__(self, value='hello, world!'):
- ...: self.data = value
- ...:
- In [2]: t = Test()
- In [3]: t
- Out[3]: <__main__.Test at 0x108844e48>
- In [4]: class TestNew(object):
- ...: def __init__(self, value='hello, world!'):
- ...: self.data = value
- ...: def __repr__(self):
- ...: return 'This is __repr__:{0}'.format(self.data)
- ...:
- In [5]: tn = TestNew()
- In [6]: tn
- Out[6]: This is __repr__:hello, world!
类的特殊成员还有__init__
,__dict__
(返回类对象中的所有成员)等等,详细的可以去看说明文档。
更多入门教程链接
关于更多入门教程, 可以通过下面的链接查看.
- 微信公众号
- 关注微信公众号
- QQ群
- 我们的QQ群号
评论