人生苦短,我用Python07:面向对象
目录:
面向对象
Python和Ruby都支持函数式编程也支持面向对象编程,而Java和C#是必须创建一个对象才能进行调用函数。
首先,要明确一点,面向对象并不适用所有的场景。
顺便说一下,pickle也可以存储对象。
如何使用面向对象
在python中所有的都是对象,就像linux的所有对象即文件一样。
如何创建类
创建类使用关键字class即可。
class Person:
pass
这样我们就创建了一个Person类,Person为类名。
如何创建类中普通方法
类中可以定义方法,同样是通过def关键字来创建。对于Person,可以有吃喝的方法。
class Person:
def eat(self):
print("eat")
def drink(self):
print("drink")
这样就为Person类创建了eat和drink两种方法,并且创建的方法的第一个参数一定是self,在调用类方法的时候是类对象自己,创建的方法为普通方法,在这里就先记住这个叫普通方法即可。
如何创建对象并调用类中方法
class Person:
def eat(self):
print("eat")
def drink(self):
print("drink")
obj=Person() #创建Person对象obj
obj.eat() #obj对象执行eat方法
obj.drink() #onj对象执行drink方法
想要使用类中方法,必须先创建一个对象,才能执行类中方法。创建对象后会申请新的内存,对象会在创建一个指向类对象的指针,这个过程也称作创建实例。
执行结果
eat
drink
类方法参数传递
class Person:
def eat(self):
print("eat")
def drink(self):
print("drink")
def sing(self,song):
print(song)
obj=Person()
obj.eat()
obj.drink()
obj.sing("see you again~")
执行结果
eat
drink
see you again~
执行obj.sing("see you again~")实质上传递了两个参数,self=obj和song="see you again~",self是类自动传递的方法
类中的字段
class Person:
def eat(self):
print("eat")
def drink(self):
print("drink")
def sing(self,song):
print(song)
def show(self):
print(self.showtext)
obj=Person()
obj.showtext="I have a mouth"
obj.show()
执行结果
I have a mouth
如果不传入
obj.showtext="I have a mouth"
执行结果
AttributeError: Person instance has no attribute 'showtext'
这样我们就可以通过obj.showtext="I have a mouth"的方式为obj对象的showtext字段赋值,然后show方法就能正确输出self.showtext,即obj.showtext
面向对象的三大特性————封装
类的构造函数
字段内容相同
刚才是通过对对应赋值的方式实现,如果每个对象都有这个字段,并且字段相同,我们就可以通过构造函数将其封装到类内,通过__init__方法。
class Person:
def __init__(self):
self.showtext = "I have a mouth"
def eat(self):
print("eat")
def drink(self):
print("drink")
def sing(self,song):
print(song)
def show(self):
print(self.showtext)
obj=Person()
obj.show()
执行结果
I have a mouth
在创建类对象的时候,直接就执行了__init__方法,直接的进行了字段赋值,创建了obj对象的时候,obj.showtext = "I have a mouth"。
可以我们执行在__init__中直接定义一个print看一下
class Person:
def __init__(self):
print("This is __init__")
def eat(self):
print("eat")
def drink(self):
print("drink")
def sing(self,song):
print(song)
def show(self):
print(self.showtext)
obj=Person()
直接执行,只创建obj对象,执行结果
This is __init__
字段内容相同
此时字段是写死的,也就是showtext只能是"I have a mouth",如果每个对象都有这个字段,但是不相同,也同样是在__init__中进行使用
class Person:
def __init__(self, showtext):
self.showtext = showtext
def eat(self):
print("eat")
def drink(self):
print("drink")
def sing(self,song):
print(song)
def show(self):
print(self.showtext)
obj1=Person("I have a mouth")
obj1.show()
obj2=Person("I have two eggs")
obj2.show()
执行结果
I have a mouth
I have two eggs
这里在创建obj1对象的时候,传入的self=obj1和showtext="I have a mouth",然后通过__init__的构造函数进行了创建,obj2同理。
关于销毁内存中对象
销毁内存中对象使用的是__del__方法,不需要我们自己进行定义。
什么时候适合面向对象编程
函数式编程
通过罗列方法(函数),然后通过传参调用实现功能
举个栗子:
def a(num):
pass
def b(sum):
pass
a(1)
b(2)
面向对象式编程
创建类,在类中封装方法和属性,然后通过创建对象进行调用
举个栗子
class c:
def a(num):
pass
def b(sum):
pass
obj=c()
obj.a(1)
obj.b(2)
对比上述两种编程方式
函数式编程就是面向过程的方式,对于实现简单的流程性工作比较适用,一般python脚本就可以这样进行编写。而面向对象的方式,在需要重复调用,拥有相同属性,相同方法的一类对象的时候,就需要创建类。
面向对象的三大特性————继承
如何继承
对于Person这个类,还可以更细分,动植物还分界门纲目科属种呢。这边我们把Person进行分类为European和African,European和African都属于Person类,肯定也有Person的所有特性,我们就可以把Person类中字段和方法都继承过来,变成自己的属性。
class Person:
def __init__(self, showtext):
self.showtext = showtext
def eat(self):
print("eat")
def drink(self):
print("drink")
def sing(self,song):
print(song)
def show(self):
print(self.showtext)
class European(Person):
pass
class African(Person):
pass
european = European("I am a European")
european.show()
执行结果
I am a European
可以看到我们创建看了一个European对象,通过继承的方式,实现了PERSON中的方法show()。因为继承的关系,也必须在创建的时候添加一个参数,传递给showtext,否则就会报错。不过可以通过一下方式解决
def __init__(self, showtext=None):
self.showtext = showtext
再回来说继承,此时Person就为European的父类,而European就为Person的子类,这是俗称,学名是Person就为European的基类,而European就为Person的派生类。
刚才的European类,使用的Person类的方法,我们也可以给European类创建自己的方法。
class Person:
def __init__(self, showtext=None):
self.showtext = showtext
def eat(self):
print("eat")
def drink(self):
print("drink")
def sing(self, song):
print(song)
def show(self):
print(self.showtext)
class European(Person):
def proud(self):
print("The British empire long live")
class African(Person):
pass
european = European()
european.proud()
执行结果
The British empire long live
可以看到我们也可以为子类创建方法
子类和父类有同名方法的问题
在子类中可以创建方法,就会有子类和父类中有同名方法的情况,这边我们为European类创建eat()方法,看一下执行结果
class Person:
def __init__(self, showtext=None):
self.showtext = showtext
def eat(self):
print("eat")
def drink(self):
print("drink")
def sing(self, song):
print(song)
def show(self):
print(self.showtext)
class European(Person):
def proud(self):
print("The British empire long live")
def eat(self):
print("European eat")
class African(Person):
pass
european = European()
european.eat()
执行结果
European eat
可以看到是执行的子类的方法
多个类的继承
对于Python来说,一个类可以继承多个类
class a():
def f(self):
print("a")
class b():
def f(self):
print("b")
class c(a, b):
def f(self):
print("c")
obj = c()
obj.f()
执行结果
c
可以看到,c类同时继承了a和b两个类,这在java和C#中都是。并且在父类和子类都有f方法的时候,依然执行了c类中的f方法。
如果c类没有f方法呢
class a():
def f(self):
print("a")
class b():
def f(self):
print("b")
class c(a, b):
pass
obj = c()
obj.f()
执行结果
a
在c类有没有f方法的时候,优先执行了写在前边的a类的f方法。
最后是才轮到b
class a():
pass
class b():
def f(self):
print("b")
class c(a, b):
pass
obj = c()
obj.f()
执行结果
b
对于继承关系,会有很多讲究 参考的(http://www.cnblogs.com/wupeiqi/p/4493506.html)
Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
- 当类是经典类时,多继承情况下,会按照深度优先方式查找
- 当类是新式类时,多继承情况下,会按照广度优先方式查找
经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。
新式类示例
class a(object):
def f(self):
print("a")
class b(a):
pass
# def f(self):
# print("b")
class c(a):
def f(self):
print("c")
class d(b, c):
pass
# def f(self):
# print("d")
obj = d()
obj.f()
执行结果
c
经典类
class a:
def f(self):
print("a")
class b(a):
pass
# def f(self):
# print("b")
class c(a):
def f(self):
print("c")
class d(b, c):
pass
# def f(self):
# print("d")
obj = d()
obj.f()
执行结果
a
如果继承多个,self依然是指方法调用者,在上述的例子,就为obj。
super()
执行父类的方法
对于python2版本,只支持这种
class Person(object):
def __init__(self):
print("I am Person")
class European(Person):
def __init__(self):
print("I am European~")
super(European, self).__init__()
obj = European()
在python3版本,除了支持上边那种,还支持
class Person:
def __init__(self):
print("I am Person")
class European(Person):
def __init__(self):
print("I am European~")
super(European, self).__init__()
obj = European()
不仅是执行父类的构造方法,也可以执行父类的其他方法
面向对象的三大特性————多态
多态是和其他语言进行区分的,因为python在传递参数的时候不需要指定传递参数的类型。
封装,继承和多态就组成了面向对象的三大特征
静态字段和普通字段
之前通过构造函数__init__生成的字段,都是普通字段,而除了普通字段,还有静态字段
class Person:
eye = 2
def __init__(self):
self.mouth = 1
obj = Person()
print(obj.mouth)
print(obj.eye)
执行结果
1
2
直接在class下定义的字段就为普通字段,在通过对象的时候可以看到都可以进行调用,但是通过类调用的时候,只能是调用静态字段。
print Person.eye
除了调用的区别,还有就是静态字段是在类的中,在调用的时候,是通过对象中类的指针进行调用,而普通字段是在对象创建生成,在每个对象都有。如果每个对象的字段相同就设置成静态字段。
静态方法,普通方法和类方法
静态方法,普通方法和类方法都归属于类,之前我们定义的方法都是普通方法
# -*- coding=utf-8 -*-
class Person:
def eat(self):
""" 定义普通方法,至少有一个self参数 """
print("eat")
@staticmethod
def drink():
"""定义静态方法,无默认参数"""
print("drink")
@classmethod
def sing(cls):
"""定义类方法,至少有一个cla参数"""
print(cls)
# 调用普通方法
obj = Person()
obj.eat()
# 调用静态方法
Person.drink()
# 调用类方法
Person.sing()
执行结果
eat
drink
__main__.Person
一般通过类访问的为静态字段,静态方法和类方法,而一般通过对象访问的为普通字段和类方法
特征
特征和方法类似,通过property装饰
# -*- coding=utf-8 -*-
class Person:
def eat(self):
""" 定义普通方法,至少有一个self参数 """
print("eat")
@property
def jump(self):
"""定义特征"""
print("jump")
# 调用普通方法
obj = Person()
obj.eat()
# 调用特征
obj.jump
执行结果
eat
jump
可以看到执行jump的时候不用加后边的括号就能执行,但是这样也无法传递参数了。只是在调用的时候无法进行传参,但是jump可以使用类中的字段进行调用
# -*- coding=utf-8 -*-
class Person:
def __init__(self, name):
self.name = name
def eat(self):
""" 定义普通方法,至少有一个self参数 """
print("eat")
@property
def jump(self):
"""定义特征"""
print("%s jump" % self.name)
# 调用普通方法
obj = Person("why")
obj.eat()
# 调用特征
obj.jump
执行结果
eat
why jump
另外属性在定义的时候也可以定义返回值
@property
def jump(self):
"""定义属性"""
jumptext = "%s jump" % self.name
return jumptext
属性一般的作用就是在内部进行一系列计算,将计算结果返回,不过也可以通过方法来实现
设置特征
class Person(object):
eye = 2
def __init__(self):
print("I am Person")
@property
def eat(self):
print "eat"
@eat.setter
def eat(self, food):
print "eat %s" % food
obj = Person()
obj.eat
obj.eat = "noodles"
执行结果
I am Person
eat
eat noodles
类的成员修饰符
方法和字段都是成员,成员分为公有成员和私有成员,我们之前创建的都是公有成员,就是任何地方都能进行访问的,而私有成员是只能类内部才能访问
class Person:
a = "a"
__b = "b"
pass
print Person.a
print Person.__b
执行结果
a
AttributeError: class Person has no attribute '_b'
可以看到a字段可以获取到,但是_b字段却返回不存在
- 对于公有静态字段,类内部,类,派生类都能进行访问,而自由静态字段,只能类内部能进行访问
- 对于公有普通字段,类内部,对象,派生类都能进行访问,而自由静态字段,只能类内部能进行访问
不过有一种方法可以强制访问私有成员,不过不推荐这么做
class Person:
a = "a"
__b = "b"
pass
obj = Person()
print obj._Person__b
执行结果
b
不过可以通过类内部方法调用,然后传出
class Person:
a = "a"
__b = "b"
def c(self):
print self.__b
obj = Person()
obj.c()
执行结果
b
对于类也是一样
class Person:
def a(self):
print "a"
def __b(self):
print "b"
def c(self):
self.__b()
obj = Person()
obj.a()
obj.c()
obj.__b()
执行结果
a
b
AttributeError: Person instance has no attribute '__b'
类的特殊成员
已经介绍过的特殊成员
- __init__ 构造函数
- __del__ 销毁函数
__doc__
class Person:
"""This is Person"""
a = "a"
__b = "b"
pass
print Person.__doc__
执行结果
This is Person
__call__
class Person:
def __call__(self, *args, **kwargs):
print "call"
obj = Person()
obj()
执行结果
call
可以看到对象后直接接括号就执行调用了__call__方法,当然,也就可以直接"Person()()"调用
__getitem__
class Person:
def __getitem__(self, item):
print(item)
obj = Person()
obj["key"]
执行结果
key
__setitem__
class Person:
def __setitem__(self, key, value):
print(key, value)
obj = Person()
obj["key"]="value"
执行结果
('key', 'value')
__delitem__
class Person:
def __delitem__(self, key):
print(key)
obj = Person()
del obj["key"]
执行结果
key
当然也可以切片操作,对应的方法__getslice__,__setslice__和__delslice__,分别通过obj[1:3],obj[1:3] = [11,22,33]和del obj[1:3]
__iter__
class Person:
def __iter__(self):
yield 1
yield 2
yield 3
obj = Person()
for i in obj:
print(i)
__metaclass__
__metaclass__指定类由什么类创建类对象,默认情况下是通过type类实例化产生。
__dict__
获取类或者对象的所有成员
class Person(object):
eye = 2
def __init__(self):
print("I am Person")
print Person.__dict__
执行结果
{'__module__': '__main__', 'eye': 2, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, '__init__': <function __init__ at 0x00000000032C2A58>}
类的反射
class Person(object):
eye = 2
def __init__(self):
print("I am Person")
r = hasattr(Person, "eye")
print r
执行结果
True