Python-Task10 类与对象

Python-Task10 类与对象

学习内容

廖雪峰老师python教程https://www.liaoxuefeng.com/wiki/1016959663602400


类和实例

面向对象最重要的概念是类(class)和实例(instance)

类:抽象的模板

实例:根据类创建出来的一个个具体的对象,各个实例的数据都相互独立。

定义类:

class Student(object):
    pass

class是定义类的关键字,Student是类名,通常首字母大写,(object)是该类是从哪一类继承下来的。没有合适的就选object类,这是所有类都会最终继承的。

创建实例:

linda=Student()
>>> linda
<__main__.Student object at 0x00000285B0435FD0>

创建实例通过类名+()实现。

>>> linda.sex='girl'
>>> linda.sex
'girl'

可以自由给实例绑定属性。

通过__init__方法我们可以在创建实例的时候,强制将我们认为必要的属性绑定上去。

class Student(object):
    def __init__(self,name,score):
        self.name=name
        self.score=score
s=Student('linda',59)

__init__方法第一个参数永远是self ,表示创建的实例本身。各种属性绑定到self。

用了__init__方法之后,创建实例的时候就不能传空参了。self不用传,python解释器会自动传进去。

在类中定义的函数和普通函数只有一点不同,就是类的函数嗲一个参数,永远是实例变量self。并且调用时,不需要传该参数。

数据封装

直接在类的内部定义访问数据的函数,这样就把数据给封装了起来。我们成这一些封装数据的函数为类的方法。

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) )
s=Student('linda',59)
s.print_score()

这样我们就可以把数据和逻辑封装起来,不必在乎内部实现细节了。

访问限制

如果我们不希望内部属性被外部访问,我们可以在属性名前面加上__这样该属性就变成了私有变量(private)。这样就能确保外部代码不能随意修改对象内部状态了。

我们可以定义访问方法和修改方法,让外部访问和修改内部变量。这样的好处是可以通过对参数检查,避免无效从参数。

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

    def get_score(self):
        return self.__score
    def set_score(self,score):
        if 0<=score <= 100:
            self.__score=score
        else:
            raise ValueError('bad score')
s=Student('linda',99)
s.set_score(80)
print(s.get_score())

变量名前是一个_name的形式,意思是:岁仍然我可以被访问,但是,请把我视为私有变量,不能随意访问。

双下划线下面变量是私有变量,但也不是完全不能外部访问,实际上python解释器是把__name变量自动转换成了_ _ Student_ _ _name变量(不同解释器不同),但即使这样也不要这么做。一切靠自觉!

继承和多态

在创建一个class的时候,我们可以从某个现有class继承,新class叫做子类,被继承的class叫做基类、父类或者超类。

子类最大的好处是获得了父类的全部功能。也可以给子类单独增加方法。我们还能对一些继承下来的方法做该井。当子类和父类有相同方法时候,子类会覆盖父类的同名方法。

class Animal(object):
    def run(self):
        print('Animal is runing')
class Dog(Animal):
    def eat(self):
        print('eating')
class Cat(Animal):
    def run(self):       
        print('Cat is runing')
dog=Dog()
dog.run()
dog.eat()
cat=Cat()
cat.run()
Animal is runing
eating
Cat is runing

我们定义一个class的时候,实际上也是定义了一种数据类型。在继承关系中,如果实力的数据类型是某个子类,那么他的数据类型也可以被看做是父类,反之,不行。

多态的好处就是,当我们需要传入Dog,Cat,Tortoise时,我们只需要接收Animal类型就可以了。因为Dog,Cat,Tortoise都是Animal类型。因为Animal有run方法,所以传入Animal类或其子类,就会自动调用实际类型的run()方法。调用方只管调用,不管细节。

开闭原则:

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

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

静态语言(如java),如果需要传入是animal类型的话,那么传入对象必须是animal 类型或者他的子类。

动态语言(python),不一定是animal类型,只要确保传入对象有run()方法就好。不需要严格的继承体系,“看起来像”就好。这就是动态语言的“鸭子类型”

file-like object是一种鸭子类型,只要有read()方法就会被当成fille-like object

获取对象信息

使用type()判断对象类型

判断两个变量的type是否相同我们可以用

type(123)==type(456)
type(123)==int

判断一个对象是否是函数,需要引入types模块中定义的常量

>>> import types
>>> def fn():
...     pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True
isinstance()判断class类型

假如继承关系是

object -> Animal -> Dog -> Husky

那么

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

所以,isinstance()可以判断一个对象是否是该类型本身,或者位于该类型的父继承链上。

isinstance()还可以判断变量是某些类型中的一种

>>> isinstance([1,1,1,1],(list,tuple))
True
使用dir()获取一个对象的所有属性和方法
>>> dir(123)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__'.... 'numerator', 'real', 'to_bytes']

__xxx__属性和方法在Python中都是有特殊的用途。例如,使用len()函数调用一个对象的长度,实际上在len

函数他会自动调用该对象的__len__()方法。

剩下的都是普通属性和方法。

我们还能配合getattr()setattr()以及hasattr()直接操作一个对象。

获取对象的属性

>>> 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
>>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404  选择传入defalut参数
404

获取对象的方法

>>> 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()是一样的
81
实例属性和类属性

实例属性可以通过实例变量或者self变量绑定

类属性归类所有。但所有该类的实例都可以访问,下面的例子很清楚的说明了之间的关系:

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

注意事项:类属性和实例属性千万不要同名,相同名称,实例属性会屏蔽掉类属性。

小结

在python中,类的实例可以任意绑定属性。类中函数(方法)第一个参数的都是self,调用时,不用传入self。其他和普通函数一样。有了方法就可以实现数据封装。

变量名前加上双下划线表示私有变量,外部不能访问。强制访问不建议。一切靠自觉!

继承可以把父类功能拿过来,可以新增也可以覆盖。动态语言的鸭子类型决定了继承不是必须的。

实例属性属于各个实例,类属性归类所有,所有实例共享一个属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值