Python中的面向对象编程(上)

Python中的面向对象编程(抽象、封装、继承、多态)


面向对象编程简单说就是将问题抽取成为对象集合,再进行编程实现问题的解决。
一个特别有趣的面向对象的介绍:

定义类与创建实例

利用【class】来定义类:

class Person(object):
    pass

在Python中类名一般以大写字母开头,后面的(object)表示该类是从哪个类继承下来的。定义一个Person类,并创建出两个实例,打印出实例:

class Person(object):
    pass

xiaoming = Person()
xiaohong = Person()

print(xiaoming)
print(xiaohong)

>> <__main__.Person object at 0x000001FEE1E22EB8>
>> <__main__.Person object at 0x000001FEE1E222E8>

可以给一个实例变量绑定属性,比如,给xiaoming和xiaohong绑定name、grade和birth:

class Person(object):
    pass

xiaoming = Person()
xiaoming.name = 'Xiao Ming'
xiaoming.birth = '2018-05-23'
xiaohong = Person()
xiaohong.name = 'Xiao Hong'
xiaohong.grade = 2 # 绑定的属性可以不同
print(xiaoming.name) # 打印实例的相应属性
print(xiaoming.birth)
print(xiaohong.name)
print(xiaohong.grade)

>> Xiao Ming
>> 2018-05-23
>> Xiao Hong
>> 2

在创建实例的时候,可以把一些我们认为必须绑定的属性强制填写进去,为Person类添加一个特殊的__init__() 初始化方法,在创建实例的时候就把name、birth、grade绑定:

class Person(object):
    def __init__(self, name, birth, grade):
        self.name = name
        self.birth = birth
        self.grade = grade
xiaoming = Person('Xiao Ming', '2018-05-23', 0)
xiaohong = Person('Xiao Hong', '2010-10-24', 2)

print(xiaoming.name)
print(xiaohong.birth)

>> Xiao Ming
>> 2010-10-24

注:init 前后分别有两个下划线
当然,此方法也可以接受任意关键字参数,并把他们都作为属性赋值给实例:

class Person(object):
    def __init__(self, name, birth, grade, **kw):
        self.name = name
        self.birth = birth
        self.grade = grade
        for k, v in kw.items():
            setattr(self, k, v)

xiaoming = Person('Xiao Ming', '2018-05-23', 0, job='Student')
xiaohong = Person('Xiao Hong', '2010-10-24', 2, gender='Female')


print(xiaoming.birth)
print(xiaoming.job)
print(xiaohong.gender)

>> 2018-05-23
>> Student
>> Female

封装

面向对象编程的一个重要特点就是数据封装。封装之后,从外部看对应的类就只知道创建实例需要给出哪些属性,但具体如何“工作”的是不知道的,比如我们可以通过一个函数来打印一个人的出生日期:

class Person(object):
    def __init__(self, name, birth, grade, **kw):
        self.name = name
        self.birth = birth
        self.grade = grade
        for k, v in kw.items():
            setattr(self, k, v)

xiaoming = Person('Xiao Ming', '2018-05-23', 0, job='Student')
xiaohong = Person('Xiao Hong', '2010-10-24', 2, gender='Female')

def print_score(std):
    print('%s: %s' % (std.name, std.birth))

print_score(xiaoming)

>> Xiao Ming: 2018-05-23

将数据封装后如下:

class Person(object):
    def __init__(self, name, birth, grade, **kw):
        self.name = name
        self.birth = birth
        self.grade = grade
        for k, v in kw.items():
            setattr(self, k, v)

    def print_birth(std):
        print('%s: %s' % (std.name, std.birth))

xiaoming = Person('Xiao Ming', '2018-05-23', 0, job='Student')
xiaohong = Person('Xiao Hong', '2010-10-24', 2, gender='Female')

xiaoming.print_birth()

>>Xiao Ming: 2018-05-23

访问限制

对于之前我们所定义的class是可以通过外部代码自由修改的,如:

class Person(object):
    def __init__(self, name, birth, grade):
        self.name = name
        self.birth = birth
        self.grade = grade

xiaoming = Person('Xiao Ming', '2018-05-23', 0)
xiaohong = Person('Xiao Hong', '2010-10-24', 2)

print(xiaoming.grade)
xiaoming.grade = 9
print(xiaoming.grade)

>> 0
>> 9

那么如何让内部的属性不被外部访问呢?Python对属性的权限控制是通过以“双下划线开头【__】”实现的,这样的实例变量的属性就只有内部可以访问,外部不能访问,所以来操作一波:

class Person(object):
    def __init__(self, name, birth, grade):
        self.__name = name
        self.__birth = birth
        self.__grade = grade

xiaoming = Person('Xiao Ming', '2018-05-23', 0)
xiaohong = Person('Xiao Hong', '2010-10-24', 2)

print(xiaoming.__grade)

>> Traceback (most recent call last):
  File "F:/Python/filter/sorted.py", line 158, in <module>
    print(xiaoming.__grade)
AttributeError: 'Person' object has no attribute '__grade'

可见,此时已经无法从外部访问实例变量的属性了,就让代码保护起来了,但是如果想获取该怎么办呢?当当当,可以给类增加【get_grade】函数(这个函数就叫做实例的方法)啊:

class Person(object):
    def __init__(self, name, birth, grade):
        self.__name = name
        self.__birth = birth
        self.__grade = grade

    def get_grade(self):  # 实例的方法的第一个参数永远是self
        return self.__grade

xiaoming = Person('Xiao Ming', '2018-05-23', 0)
xiaohong = Person('Xiao Hong', '2010-10-24', 2)

print(xiaoming.get_grade())  # self不需要显式传入

>> 0

那么,可以在外部访问,自然也是可以做到在外部修改的,在增加一个【set_grade】就可以了:

class Person(object):
    def __init__(self, name, birth, grade):
        self.__name = name
        self.__birth = birth
        self.__grade = grade

    def get_grade(self):
        return self.__grade

    def set_grade(self, grade):
        self.__grade = grade

xiaoming = Person('Xiao Ming', '2018-05-23', 0)
xiaohong = Person('Xiao Hong', '2010-10-24', 2)

print(xiaoming.get_grade())
xiaoming.set_grade(9)
print(xiaoming.get_grade())

>> 0
>> 9

虽然以单“下划线开头的属性【_xxx】”也可以被外部访问和修改,但是,按照习惯,他们不应该被外部访问。
注意:变量名以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量。
实际上方法也是一个属性,所以它也可以动态地添加到实例上,用【types.MethodType() 】可以把一个函数变成一个方法:

import types

def fn_get_grade(self):
    if self.score >= 80:
        return 'A'
    if self.score >= 60:
        return 'B'
    return 'C'

class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

xiaoming = Person('Xiao Ming', 90)
xiaohong = Person('Xiao Hong', 60)

xiaoming.get_grade = types.MethodType(fn_get_grade, xiaoming)

print(xiaoming.get_grade())
print(xiaohong.get_grade())  # xiaohong实例并没有绑定get_grade

>> A
>> AttributeError: 'Person' object has no attribute 'get_grade' 

和属性类似,方法也分实例方法类方法。那么,如何在class中定义类方法呢?答案是通过一个【@classmethod】来实现让该方法绑定在类上,类方法的第一个参数将传入类本身,通常将参数名命名为 cls:

class Person(object):
    count = 0

    @classmethod
    def how_many(cls):
        return cls.count

    def __init__(self, name):
        self.name = name
        Person.count = Person.count + 1

print(Person.how_many())
xiaoming = Person('Xiao Ming')
print(Person.how_many())
xiaohong = Person('Xiao Hong')
print(Person.how_many())
print(xiaoming.how_many())

>> 0
>> 1 
>> 2
>> 2

创建类属性

类是模板, 而实例则是根据类创建的对象。
绑定在一个实例上的属性不会影响其他实例,但是如果给类绑定一个属性,则所有的实例均可以访问这个属性,以下用给Person绑定一个China的属性为例:

class Person(object):
    L = 'China'

    def __init__(self, name):
        self.name = name

xiaoming = Person('Xiao Ming')

print(Person.L)
print(xiaoming.L)

>> China
>> China

我们得知:如果给类绑定一个的属性,则这个类下创建的实例都将拥有这个属性,当然因为Python是动态语言,所以类的属性也是可以动态添加和修改的。
既然类和实例都可以绑定属性,那么如果两个属性的名字冲突了怎么办?例:

class Person(object):
    L = 'China'

    def __init__(self, name):
        self.name = name

xiaoming = Person('Xiao Ming')
xiaohong = Person('Xiao Hong')

print(Person.L)
print(xiaoming.L)
print(xiaohong.L)

xiaoming.L = 'Earth'  # 给xiaoming绑定一个L属性

print(Person.L)
print(xiaoming.L)
print(xiaohong.L)

>> China
>> China
>> China
>> China
>> Earth
>> China

我们发现,结果只有xiaoming的属性改变了。即证明当我们给xiaoming绑定一个与类同名字的属性时,它只是改变了xiaoming自己的L属性,而类的L属性并没有被改变。可见,当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问。当把xiaoming的L属性删掉之后,xiaoming的L属性就又变成了‘China’:

...
del xiaoming.L
print(xiaoming.L)

>> China

所以,在实例上是无法修改类属性的。

拖了好久,用了好长时间学习,实在是基础太差,但我一直在努力,加油

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值