第八章 面向对象编程

版权声明:版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/qq_26489189/article/details/82751877

Python3.5笔记

第8章 面向对象编程

面向对象术语介绍

  • 类:用来描述具有相同属性和方法的对象的集合。类定义中集合了每个对象共有的属性和方法。对象是类的示例。
  • 类变量(属性):类属性在整个实例化的对象中是公用的。类变量定义在类中,且在方法之外。类常亮通常不作为适量变量使用。类变量也称作属性。
  • 数据成员:类变量或实例变量用于处理类及其实例变量的相关数据。
  • 方法重写:如果从父类继承的方法,不能满足子类的需求,就可以对其重写,这个过程叫做方法的覆盖,也称为方法的重写。
  • 实例变量:定义在方法中的变量,只作用于当前实例的类。
  • 多态:对不同类的对象使用同样的操作。
  • 封装:对外部对象隐藏对象的工作细节。
  • 继承:即一个派生类继承基类的字段和方法。继承允许把一个派生类的对象作为一个基类对象对待,以普通类为基础建立专门的类对象。
  • 实例化:创建一个类的实例,类的具体对象。
  • 方法:类中定义的函数。
  • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

类的定义

示例:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-
class MyClass(object):
    i = '小明'
    def f(self):
        return 'hello world %s' % self.i

类定义的写法:

  • 使用class关键字,class关键字后面紧跟类名,类名通常首字母大写
  • 类名后紧跟的是(object),表明该类是从哪个类中继承而来的,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
  • 类通常包含属性和方法。在类中定义方法时,第一个参数必须是self。除第一个参数外,类的方法和普通函数没有什么区别。

类的使用

#! /usr/bin/python3
# -*- coding:UTF-8 -*-
class MyClass(object):
    i = '小明'
    def f(self):
        return 'hello world %s' % self.i

my_class = MyClass()
print('类中的属性是:',my_class.i)
print('类中方法的返回是:',my_class.f())

输出:

类中的属性是: 小明
类中方法的返回是: hello world 小明

类的使用比函数调用多了几个操作:

  • my_class = MyClass() 这步叫做类的实例化,即创建一个类的实例。由此得到的my_class变量称为类的具体对象。
  • 调用类中定义的方法,除了self不用传递外,其他参数正常传入。
  • 类属性引用的语法为:obj.name,obj为类对象,name代表属性。

类的构造方法

在Python中,_init_()方法是一个特殊方法,在对象实例化时会被调用。这个方法的写法是:先输入两个下划线,后面接着输入init,再接着两个下划线。这个方法也叫构造方法。在定义类时,如果不显示的定义_init_()方法,则程序默认会调用一个无参的构造方法。示例如下:

class MyClass2(object):
    i = 123
    def __init__(self,name1,age1):
        self.name = name1
        self.age = age1

    def f(self):
        return 'hello %s,you are %s years old!' % (self.name,self.age)

my_class2 = MyClass2('小明','23')
print('类中的属性是:',my_class2.name)
print('类中的属性是:',my_class2.age)
print('类中方法的返回是:',my_class2.f())

输出:

类中的属性是: 小明
类中的属性是: 23
类中方法的返回是: hello 小明,you are 23 years old!

一个类中可以定义多个构造方法,但实例化类时只实例化最后的构造方法,即后面的构造方法会覆盖前面的构造方法,并且需要根据最后一个构造方法的形参进行实例化。建议一个类中,只定义一个构造方法。

类的访问权限

直接访问公有属性和方法

在类的内部有属性和方法,外部代码可以直接调用属性和方法。示例如下:

#! /user/bin/python3
# -*- coding:UTF-8 -*-
class Student(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def info(self):
        print('hello %s,you are %s years old!' % (self.name,self.age))

stu = Student('小明','23')
stu.info()
stu.name = '小张'
stu.age = '24'
stu.info()

输出:

hello 小明,you are 23 years old!
hello 小张,you are 24 years old!

如何定义和访问私有属性

要让类的内部属性不被外部访问,可以在属性名称前加两个下划线__。在Python中,实例的变量名如果以两个下划线开头,就会变成私有变量,只有内部可以访问,外部不能访问。此时,如果直接访问就会出错。示例如下:

#! /user/bin/python3
# -*- coding:UTF-8 -*-
class Student(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age

    def info(self):
        print('hello %s,you are %s years old!' % (self.__name, self.__age))

stu = Student('小明','23')
stu.info()
stu.__name = '小张'
stu.__age = '24
stu.info()

输出:

Traceback (most recent call last):
  File "D:/pyspace/hellopython/Chapter8.py", line 60, in <module>
    stu.info()
  File "D:/pyspace/hellopython/Chapter8.py", line 57, in info
    print('hello %s,you are %s years old!' % (self.name,self.age))
AttributeError: 'Student' object has no attribute 'name'

这时,可以通过为类增加get_attrs()方法,获取类中的私有变量。可以为类添加set_attrs()方法,修改类中的私有变量。示例如下:

class Student(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age

    def info(self):
        print('hello %s,you are %s years old!' % (self.__name,self.__age))

    def get_name(self):
        return self.__name

    def set_name(self,name):
        self.__name = name

    def get_age(self):
        return self.__age

    def set_age(self,age):
        self.__age = age
stu = Student('小明','23')
stu.info()
stu.set_name('小张')
print('修改后的姓名是:',stu.get_name())
stu.set_age('24')
print('修改后的年龄是',stu.get_age())

输出:

hello 小明,you are 23 years old!
修改后的姓名是: 小张
修改后的年龄是 24

如何定义和访问私有方法

通过在方法名前加两个下划线,可以把方法定义为私有方法。私有方法不能通过外部代码直接访问,只能在类中通过公共方法调用。实例如下:

#! /user/bin/python3
# -*- coding:UTF-8 -*-
class Student(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __info(self):
        print('hello %s,you are %s years old!' % (self.name,self.age))

    def foo(self):
        self.__info()

stu = Student('小明','23')
stu.foo()

输出:

hello 小明,you are 23 years old!

继承

当我们定义一个class时,可以从某个现有的class继承,定义的新class称为子类(SubClass),而被继承的class称为基类、父类或者超类(BaseClass、Super Class)。继承的格式如下:

class DerivedClassName(BaseClassName):
    statemnt-1
    ...
    statement-n

继承语法class子类名(基类名)时,基类名写在括号内,在元组中指明。特点如下:

  1. 在继承中,基类的构造方法不会被自动调用,需要在子类的构造方法中专门调用。
  2. 在调用基类的方法时,需要加上基类的类名前缀,并带上self参数变量。区别于在类中调用普通方法时,不需要带self参数。
  3. 在Python中,首先查找对应类型的方法,如果在子类中找不到对应的方法,才到基类中逐个查找。
class Animal(object):
    def run(self):
        print('animal is running....')

class Dog(Animal):
    pass

class Cat(Animal):
    pass

dog = Dog()
dog.run()
cat = Cat()
cat.run()

输出:

animal is running....
animal is running....

子类不能继承父类的私有方法,也不能调用父类的私有方法。

多态

当子类和父类有相同的方法时,子类的方法会覆盖父类的方法,在方法调用时,总是会首先调用子类的方法,这种情况就是多态。示例如下:

#! /user/bin/python3
# -*- coding:UTF-8 -*-
class Animal(object):
    def run(self):
        print('animal is running....')

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

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

dog = Dog()
dog.run()
cat = Cat()
cat.run()
print('dog是否是animal类型',isinstance(dog,Animal))
print('dog是否是dog类型',isinstance(dog,Animal))

输出:

dog is running....
cat is running...
dog是否是animal类型 True
dog是否是dog类型 True

例如上面的示例中,dog和cat分别继承了animal,并且分别定义了自己的run方法,最后调用的是各自的run方法。

使用多态的好处是:当我们需要传入Dog,Cat等子类对象时,只需要接收父类Animal对象就可以了,因为Dog,Cat等都是Animal类型,按照Animal类型传入参数即可。由于Animal类型都有run()方法,因此传入的类型只要是Animal类或者是Animal的子类,都会自动调用实例类型的方法。

多重继承

上一节讲述的是单继承,Python还支持多重继承。多重继承的类定义如下:

class DerivedClassName(Base1,Base2,Base3):
    statment-1
    ...
    statement-n

可以看到,多重继承就是有多个基类(父类或者超类)。

需要注意圆括号中父类的顺序,若父类中有相同的方法名,在子类使用时未指定,Python会从左到右搜索。若方法在子类中未找到,则从左到右查找父类中是否包含此方法。

获取对象信息

使用type()函数

判断基本类型可以用type()函数判断。如:

print('-----------type()函数-----------')
print(type('abc'))
print(type(123))
print(type(None))
print(type(abs))

print(type('abc')==str)
print(type(123)==int)

print('-----------判断一个对象是否是函数-----------')
import types
def func():
    pass

print(type(func)==types.FunctionType)
print(type(abs)==types.BuiltinFunctionType)
print(type(lambda x,y,z:x+y+z)==types.LambdaType)
print(type(x for x in range(1,10))==types.GeneratorType)

输出:

-----------type()函数-----------
<class 'str'>
<class 'int'>
<class 'NoneType'>
<class 'builtin_function_or_method'>
True
True
-----------判断一个对象是否是函数-----------
True
True
True
True

使用isinstance()函数

使用isinstance()函数可以告诉我们一个对象是否是某种类型。

print('dog是否是animal类型',isinstance(dog,Animal))
print('dog是否是dog类型',isinstance(dog,Animal))

输出

dog是否是animal类型 True
dog是否是dog类型 True

使用dir()函数

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

print(dir('abc'))

输出:

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

类的专有方法

_str_

相当于java中的to_string()方法。如:

class Student(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age

    def __str__(self):
        return '学生名称是:%s,学生的年龄是:%s' % (self.__name,self.__age)

print(Student('小明','23'))
stu_abc = Student('小宇','24')
print(stu_abc)

输出:

学生名称是:小明,学生的年龄是:23
学生名称是:小宇,学生的年龄是:24

注意:__str__方法必须返回一个字符串

_iter_

如果要将一个类用于for…in循环,类似list或者tuple一样,就必须实现一个_iter_()方法。该方法返回一个迭代对象,Python的for循环会不断调用迭代对象的__next()方法,获得循环的下一个值,直到遇到StopIteration错误时退出循环。如:

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a、b

    def __iter__(self):
        return self # 实例本身就是迭代对象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration();
        return self.a # 返回下一个值

for n in Fib():
    print(n)

输出:

1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025

_getitem()_

要像list一样按照下标取出元素,需要实现__getitem()__方法,示例如下:

class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a
    
print(Fib()[3])

输出:

3

_getattr_

使用__getattr__方法动态返回一个属性。如:

class Student1(object):

    def __getattr__(self, attr):
        if attr == 'score':
            return 95


stu1 = Student1()
print(stu1.score)

输出:

95

_call_

通过给一个类定义__call__方法,可以直接对实例进行调用并得到结果。__call__还可以定义参数。对实例进行直接调用就像对一个函数调用一样,完全可以把对象看成函数,把函数看成对象。因为这两者本来就没有根本区别。如果把对象看成函数,函数本身就可以在运行期间动态创建出来,因为类的实例都是运行期间创建出来的。这样一来,就模糊了对象和函数的界限。实例如下:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-
class Student(object):
    def __init__(self,name):
        self.name = name

    def __call__(self, *args, **kwargs):
        print('姓名是:%s' % self.name)

stu2 = Student('小明')
stu2()

输出:

姓名是:小明

可以使用Callable()函数,判断一个对象是否可以被调用。实例如下:

print(callable(Student('小明')))
print(callable(Student1()))
print(callable(max))
print(callable([1,2,3]))

输出:

True
False
True
False

牛刀小试——出行建议

小智今天想出去,但不清楚今天的天气是否适合出行,需要一个给他提供建议的程序,程序建议要求输入daytime和night,根据可见度和温度给出出行建议和使用的交通工具,需要考虑需求变更的可能。

需求分析:使用本章所学的封装、继承、多态比较容易实现,由父类封装查看可见度和温度的方法,子类继承父类。如果有需要,子类可以覆盖父类的方法,做自己的实现。子类也可以自定义方法。

class WeatherSeach(object):
    def __init__(self,input_daytime):
        self.input_daytime = input_daytime

    def search_visible(self):
        visible = 0
        if self.input_daytime == 'daytime':
            visible = 6
        elif self.input_daytime == 'night':
            visible = 2
        return visible

    def search_temperature(self):
        temperature = 0
        if self.input_daytime == 'night':
            temperature = 16
        elif self.input_daytime == 'daytiem':
            temperature = 26

        return temperature

class OutAdvice(WeatherSeach):
    def __init__(self,input_daytime):
        WeatherSeach.__init__(self,input_daytime)

    def search_temperature(self):
        we_use = 'none'
        if self.input_daytime == 'daytime':
            we_use = '骑自行车'
        elif self.input_daytime == 'night':
            we_use = '开车'
        return we_use

    def take_advice(self):
        we_use = self.search_temperature()
        visible = self.search_visible()
        if visible >= 6:
            print('今天天气不错,可以%s出行' % we_use)
        elif 0 < visible <= 2:
            print('今天天气不好,只能%s出行' % we_use)
        else:
            print('输入的内容有误,无法给出相应的建议!')

oa = OutAdvice('night')
oa.take_advice()

输出

今天天气不好,只能开车出行

问题解答

双下划线开头的实例变量一定不能从外部访问吗?

答:不是,不能直接访问的原因是Python解释器对外把__score变量变成了_Student__score,所以,仍然可以通过Student__score访问变量__score。如:

class Student(object):
    def __init__(self,name,score):
        self.__name = name
        self.__socre = score

    def info(self):
        print('学生的姓名是:%s,成绩是:%s' % (self.__name,self.__socre))

    def get_score(self):
        return self.__socre

stu3 = Student('小宇','94')
stu3.info()
#print('分数是:',stu3._Student__score) #报错
print('分数是:',stu3.get_score())

输出:

学生的姓名是:小宇,成绩是:94
分数是: 94

方法和函数的区别

答:在Python中,函数并不依附于类,在不在类中定义。而方法依附于类,定义在类中,本质上还是一个函数。为便于区分,我们把类中的函数叫做方法,不依赖于类的函数仍然称为函数。

为什么要使用类

答:在Python中,借助继承、封装、多态三大特性,使用类可以更好的对一类实物进行管理,可以将具有相同功能和行为的事物封装为一个类,其他具有相同特性的事物直接继承类,即可获得父类封装好的方法。同时,子类可以覆盖父类的方法,以满足特定的功能需求,子类也可以扩展自己的功能。使用类可以更好的实现代码的复用和扩展。

猜你喜欢

转载自blog.csdn.net/qq_26489189/article/details/82751877