廖雪峰python3.6教程笔记8-Python基础之面向对象编程

面向过程编程:把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。

面向对象编程:把计算机程序视为一组对象的集合,把对象作为程序的基本单元,一个对象包含了数据和操作数据的方法。而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

一 类和实例

二数据封装、继承和多态

三访问权限

四获取对象信息

五实例属性和类属性

一类和实例

类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;

方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;

通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。

这里有个很搞笑的关于类,实例,对象,方法的例子,看了应该很容易理解:

https://www.liaoxuefeng.com/discuss/001409195742008d822b26cf3de46aea14f2b7378a1ba91000/001515567729282998199544f8241589704f6de78fcaedf000

例1: 

>>> class Student(object):  //类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
...     def __init__(self, name, score):  //类可以起到模板的作用,因此,可以在创建实例的时候,把一些必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
...         self.name = name
...         self.score = score
...     def print_score(self):
...         print('%s: %s' % (self.name, self.score))
...     def get_grade(self):   //定义一个方法,除了第一个参数是self外,其他和普通函数一样
...         if self.score >= 90:
...             return 'A'
...         elif self.score >= 60:
...             return 'B'
...         else:
...             return 'C'
...
>>> lisa = Student('Lisa', 99)   //有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去. 
// lisa= Student()   如果没有__init__方法,则可以用直接创建实例,
//lisa.name = ‘Lisa’  后面一个个绑定属性
//lisa.score = 99 
>>> lisa      //变量lisa 指向的就是一个Student的实例,后面的0x10ddc9f60是内存地址,每个object的地址都不一样
<__main__.Student object at 0x10ddc9f60>
>>> Student    //Student本身则是一个类。
<class '__main__.Student'>
>>> lisa.print_score
<bound method Student.print_score of <__main__.Student object at 0x10ddc9f60>>
>>> lisa.name
'Lisa'
>>> lisa.score
99
>>> lisa.print_score()
Lisa: 99
>>> lisa.get_grade()
'A'

和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。 

二 数据封装、继承和多态

数据封装、继承和多态是面向对象的三大特点。

数据封装

我们从外部看Student类,就只需要知道,创建实例需要给出name和score,而如何使用,都是在Student类的内部定义的。类的方法就负责将这些数据和逻辑被“封装”起来了,这些方法是和Student类本身是关联起来的,直接从类的内部定义访问数据。调用很容易,隐藏了复杂的内部逻辑。

继承和多态:

当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

最大的好处是子类获得了父类的全部功能。也可以对子类增加一些方法,

允许我们对代码做一点改进,当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。

例2

>>> class Animal(object):
...     def run(self):
...         print('Animal is running...')
...
>>> class Dog(Animal):
...     pass
...
>>> dog = Dog()
>>> dog.run()    //子类获得了父类的全部功能
Animal is running...
>>> class Dog(Animal):
...     def run(self):    //也可以对子类增加一些方法
...         print('Dog is running...')
...
>>> dog = Dog()
>>> dog.run()    
Dog is running...
>>> isinstance(dog, Dog)   //一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类
True
>>> isinstance(dog, Animal)
True
>>> def run_twice(animal):
...     animal.run()
...     animal.run()
>>> run_twice(Animal())   //编写一个函数,这个函数接受一个Animal类型的变量
Animal is running...
Animal is running...
>>> run_twice(Dog())  //只要是Animal类或者子类,就会自动调用实际类型的run()方法
Dog is running...
Dog is running...

对扩展开放:允许新增Animal子类;

对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

三 访问限制

在上例中定义的Student类,外部代码可以自由的修改实例的属性:

例3

>>> lisa.score = 80
>>> lisa.print_score()
Lisa: 80

可以将属性变为private变量,这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。

例4:   

class Student(object):
    def __init__(self, name, score):
        self.__name = name
        self.__score = score
    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))
    def get_name(self):     //外部代码要获取name和score的方法
        return self.__name
    def get_score(self):
        return self.__score
    def set_score(self, score):     //允许外部代码修改score,在方法中,可以对参数做检查,避免传入无效的参数
        if 0 <= score <= 100:
            self.__score = score
        else:
            raise ValueError('bad score')
>>> lisa.__name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'

需要注意的是,在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。

有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。  

四 获取对象信息

当我们拿到一个对象的引用时,如何知道这个对象是什么类型、有哪些方法呢?

关于对象类型:

方法一:type()主要可以判断基本类型,例如int, str, chr 等,也可以判断一个变量是指向一个函数或者类。

方法二:isinstance()判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上。(优先使用)

例5:

>>> isinstance(h, Animal)
>>> isinstance(d, Dog) and isinstance(d, Animal)
>>> isinstance([1, 2, 3], (list, tuple))

关于对象方法:

如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法

仅仅把属性和方法列出来是不够的,配合getattr()setattr()以及hasattr(),我们可以直接操作一个对象的状态:

五 实例属性和类属性

由于Python是动态语言,根据类创建的实例可以任意绑定属性。

给实例绑定属性的方法是通过实例变量,或者通过self变量:

例6:

class Student(object):
    def __init__(self, name):  
        self.name = name    //self变量
s = Student('Bob')
s.score = 90   //实例变量

类属性:直接在class中定义属性。

例7:

class Student(object):
name = 'Student'

实例属性和类属性的区别:

例8:

>>> class Student(object):
...     name = 'Student'
...
>>> s = Student() # 创建实例s
>>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
Student
>>> print(Student.name) # 打印类的name属性
Student
>>> s.name = 'Michael' # 给实例绑定name属性
>>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
Michael
>>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
Student
>>> del s.name # 如果删除实例的name属性
>>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
Student

例9 应用:为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:

>>> class Student(object):
...     count = 0
...     def __init__(self, name):
...         self.name = name
...         Student.count += 1
...
>>> Student.count
0
>>> bart = Student('Bart')
>>> Student.count
1
>>> bart = Student('Bart')
>>> Student.count
2

分析:

self.count=count+1 ,会报错,因为count不存在,要想得到count值,要用self.count.

如果是用self.count  = self.count + 1的话,虽然语法没有问题,但是count每次并不会增加。因为self.count实质上是绑定了一个新的实例属性count,已经与类中原先声明的变量“count”不是同一个了,考察实例属性与类属性区别

注意:在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值