面向对象编程--python廖雪峰

本文详细介绍了Python中的面向对象编程,包括类和实例的创建、访问限制、继承和多态,以及如何获取对象信息。重点讲解了数据封装、如何定义类和实例、如何访问和修改内部属性,以及子类覆盖父类方法的概念。面向对象的三大特点是数据封装、继承和多态。
摘要由CSDN通过智能技术生成

一,概述

  1. 面向对象编程(oop)和面向程序编程的不同
  2. 数据封装、继承和多态是面向对象的三大特点
  3. 关于类和实例

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

二,类和实例

1.步骤

  1. 定义类 
  2. 创建实例(+属性) ~属性的调用
  3. 数据封装--也叫定义类的方法 ~方法的调用

~2,3本质上都是函数,特殊之处在于1)有self这个实例变量,但是调动时不需要写;2)语法为self.***(属性)

【1】定义类

class Student(object):

class+类名,即Student(类名通常是大写开头的单词)+(object)

表示该类是从哪个类继承下来的,通常用object类,这是所有类最终都会继承的类。

【2】定义:实例属性的函数---‘创建实例’

类可以起到模板的作用,通过定义一个特殊的__init__方法,在创建实例的时候,就把namescore等属性绑上去:

class Student(object):

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

注意:

1.__init__方法的第一个参数永远是self(创建的实例本身),因此,可以把各种属性绑定到self,因为self就指向创建的实例本身。

2.__init__方法,在创建实例的时候,不能传入空的参数,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:

3.特殊方法“__init__”前后分别有两个下划线!!!

~属性的调用

>>> bart = Student('Bart Simpson', 59)
>>> bart.name
'Bart Simpson'
>>> bart.score
59

4.自由绑定属性

>>> bart.name = 'Bart Simpson'
>>> bart.name
'Bart Simpson'

【4】定义:调用实例数据的函数 --‘数据封装’or‘类的方法’

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))

定义一个方法,除了第一个参数是self外,其他和普通函数一样

~方法的调用

要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入,这里只有self一个参数,所以直接()即可。

>>> bart.print_score()
Bart Simpson: 59

1.我们从外部看Student类,就只需要知道,创建实例需要给出namescore,而如何打印,都是在Student类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节。

2.给类增加新的方法:get_grade:正常调用即可

class Student(object):
    ...

    def get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'

2.小结

~类    创建实例的模板

~实例 一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响

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

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

Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同

bart = Student('Bart Simpson', 59)
>>> lisa = Student('Lisa Simpson', 87)
>>> bart.age = 8
>>> bart.age
8
>>> lisa.age
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'age'

三,访问限制

 1.让内部属性不被外部访问

【1】可以访问的情况

>>> bart = Student('Bart Simpson', 59)
>>> bart.score
59
>>> bart.score = 99
>>> bart.score
99

 【2】让内部属性不被外部访问:属性的名称前加上两个下划线__,意为私有变量(private),只有内部可以访问,外部不能访问

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))

结果:无法从外部访问实例变量.__name实例变量.__score

>>> bart = Student('Bart Simpson', 59)
>>> bart.__name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'

2.外部代码要获取name和score

给Student类增加get_nameget_score(单_下划线)

class Student(object):
    ...

    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score

bart=Student('Bart Simpson',59)
print(bart.get_name())

3.允许外部代码修改score

给Student类增加set_score

class Student(object):
    ...

    def set_score(self, score):
        if 0 <= score <= 100:
            self.__score = score
        else:
            raise ValueError('bad score')

bart=Student('Bart Simpson',59)
print(bart.get_score()) #用get_score可以查看实例的数据
print(bart.set_score(100)) #用set_score可以更改实例的数据
print(bart.get_score()) #用get_score验证实例的数据确实已经被更改

结果:59 100 100

注意点:

1.变量名类似__xxx__(前后,双下划线):特殊变量,可以直接访问,不是private变量

_name,单下划线开头的实例变量名:虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

 __name__score(前,双下划綫):私有变量,按理说不可以访问,but见2.

2.私有变量不可以访问的本质在于:Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量

>>> bart._Student__name
'Bart Simpson'

but, 不同版本的Python解释器可能会把__name改成不同的变量名

3.错误写法注意

>>> bart = Student('Bart Simpson', 59)
>>> bart.get_name()
'Bart Simpson'
>>> bart.__name = 'New Name' # 设置__name变量!
>>> bart.__name
'New Name'

 表面上看,外部代码“成功”地设置了__name变量,但实际上这个__name变量和class内部的__name变量不是一个变量!内部的__name变量已经被Python解释器自动改成了_Student__name,而外部代码给bart新增了一个__name变量

>>> bart.get_name() # get_name()内部返回self.__name
'Bart Simpson'

四,继承和多态

1.子类

~类是可以继承的 

编写一个animal类

class Animal(object):
    def run(self):
        print('Animal is running...')

编写dog和cat类,可以直接继承animal

class Dog(Animal):
    pass

class Cat(Animal):
    pass

【1】对于Dog来说,Animal就是它的父类

【2】子类获得了父类的全部功能,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。

dog = Dog()
dog.run()

结果:Animal is running... 

~对子类增加方法

class Dog(Animal):

    def run(self):
        print('Dog is running...')

结果: Dog is running...

当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。-多态

2.多态

~特点:对扩展开放:允许新增Animal子类;对修改封闭:不需要修改依赖Animal类型的run_twice()等函数

def run_twice(animal):
    animal.run()
    animal.run()

>>> run_twice(Animal())
Animal is running...
Animal is running...

>>> run_twice(Dog())
Dog is running...
Dog is running...

 再定义一个Tortoise类型,也从Animal派生

3.静态语言VS动态语言

~定义的类本质上就是一种数据类型

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:

class Timer(object):
    def run(self):
        print('Start...')

Python的“file-like object“就是一种鸭子类型。对真正的文件对象,它有一个read()方法,返回其内容。但是,许多对象,只要有read()方法,都被视为“file-like object“。许多函数接收的参数就是“file-like object“,你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。

五,获取对象信息

确定对象的‘类型’以及‘方法’--(方法☞数据处理方式的意思)

1.type()函数

【1】return 对应的class类型

【2】可以判断基本数据类型:int str也可以判断函数:abs

>>> type(123)
<class 'int'>
>>> type('str')
<class 'str'>
>>> type(None)
<type(None) 'NoneType'>

>>> type(abs)
<class 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>

【3】可以判断两个变量的类型是否相同,判断是否为int,str,但无法判断函数

>>> type(123)==type(456)
True
>>> type(123)==int
True
>>> type('abc')==type('123')
True
>>> type('abc')==str
True
>>> type('abc')==type(123)
False

2.types模块中定义的常量--实现判断是否为某一个函数

>>> import types
>>> def fn():
...     pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True

3.isinstance()--- 总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”。

【1】继承类常用

继承关系为

object -> Animal -> Dog -> Husky

可以判断一个对象是否是某种类型

创建

>>> a = Animal()
>>> d = Dog()
>>> h = Husky()

 判断

>>> isinstance(h, Husky)
True
>>> isinstance(h, Dog)
True

【2】type可以判断的,isinstance也可以

>>> isinstance('a', str)
True
>>> isinstance(123, int)
True
>>> isinstance(b'a', bytes)
True

【3】 还可以判断一个变量是否是某些类型中的一种

判断是否是list或者tuple:

>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True

4.dir() -获得一个对象的所有属性和方法

【1】返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:

>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']

5.其他属性和方法 

【1】长度

__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:

>>> len('ABC')
3
>>> 'ABC'.__len__()
3

【2】返回小写

lower()返回小写的字符串:

>>> 'ABC'.lower()
'abc'

【3】测试对象的属性或者获取对象的方法(这话你得好好读)

getattr()setattr()以及hasattr()

>>> class MyObject(object):
...     def __init__(self):
...         self.x = 9
...     def power(self):
...         return self.x * self.x
...
>>> obj = MyObject()

 ~测试该对象的属性

>>> hasattr(obj, 'x') # 有属性'x'吗?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有属性'y'吗?
False
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
>>> hasattr(obj, 'y') # 有属性'y'吗?
True
>>> getattr(obj, 'y') # 获取属性'y'
19
>>> obj.y # 获取属性'y'
19

如果试图获取不存在的属性,会抛出AttributeError的错误:

>>> getattr(obj, 'z') # 获取属性'z'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyObject' object has no attribute 'z'

可以传入一个default参数,如果属性不存在,就返回默认值:

>>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404
404

 ~获得对象的方法(因为power是对象的方法)

>>> hasattr(obj, 'power') # 有属性'power'吗?
True
>>> getattr(obj, 'power') # 获取属性'power'
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn # fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn() # 调用fn()与调用obj.power()是一样的

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值