人生苦短,我用Python07:面向对象

时间:July 12, 2017 分类:

目录:

面向对象

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