Python学习笔记(四)面向对象编程

类和实例

原理和Java一样,学过都懂

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

定义类

class Student(object):
    pass

class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

创建实例
和Java不同是不要new

test = Student()

绑定属性

第一种方式:

test.name = 'Murphy'

name就是自己想绑定的属性,取名随意

第二种方式:
我们Java都知道,一个类要实例成对象,必须要有构造方法,我们定义类时不写构造方法,java底层都会生成一个默认的无参数的构造方法,如果写了构造方法,就不会默认生成无参构造方法,就不能直接new 类名() 了,所以一般来说, 只要写了有参的构造函数,都要写一个无参的构造函数。

在Python也有个类似的方法:
__init__方法,__init__方法的第一个参数永远是self,表示创建的实例本身,其他参数都是要绑定的属性
有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去

class Student(object):

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

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

这里有个疑思,Python的构造方法能不能重载呢?

class Student(object):

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

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


test1 = Student('Murphy',18)
test2 = Student('Murphy',18,'男')

test1报错,test2不报错,不能重载。

封装
将类的属性设置成私密的,然后提供公开的函数,你只能通过调用函数得到属性,或者将函数写在类里,你想用就通过类的实例调用
这种隐藏对象的属性和实现细节,只提供接口就叫封装。一般我们把那种专门封装各种函数的类,叫做工具类。

访问限制

上面有说将属性设置成私密的,这就来学习怎么设置。
很简单,在属性前加两个下划线__,就相当于private
举个栗子,简单的东西不多说

class Student(object):

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

    def get_name(self):
        return self.__name

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

    def get_age(self):
        return self.__age

    def set_age(self, age):
        self.__age = age

这样我们就不能直接通过实例点属性得到属性信息来,只能通过get函数得到,通过set函数设置。

继承和多态

Java继承都是用extends关键字
上面不说有谈到吗,因为不知道继承哪个,所以我们写了个object

class Student(object):
    pass

继承就是这个东东,要继承谁,把object改成谁

class Animal(object):

    def run(self):
        print('Animal is running...')
        
    def eat(self):
        print('Eating meat...')   


class Dog(Animal):

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

分析上面的代码,Animal就是父类,Dog就是子类
继承就是子类获得了父类的全部功能,例如:

Dog().eat()

Dog继承了Animal,所以它也有了eat方法。

Dog().run()

我们发现并没有执行父类的run,这种就叫重写
重写就是子类写一个和父类同名同参数的方法,这样就覆盖掉父类的方法

继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。

多态

创建了一个类的实例,其实可以说,他就是一个这个类的类型的变量
所以要说的是子类的实例,它既是属于子类类型也属于父类类型

什么叫多态?多态就是一种事物可以有多种表现形式

多态三要素

  1. 被动方必须有继承关系
  2. 子类一般都要重写父类方法
  3. 必须将主动方的功能函数的参数设置为被动方父类的类型

来个栗子:

#父类
class Car(object):
      def run(self):
         print('我会开车')
   

假如说司机会开公交车 我们需要定义个公交车的类(被动方) 然后在司机类(主动方)中添加会开公交车的的方法

假如说司机还会开卡车 我们需要再定义一个卡车的类(被动方) 然后在司机类中添加会开卡车的方法

# 公交车类
class Bus(Car):
    def run(self):
        print('我会开公交车')
# 卡车类
class Truck(Car):
    def run(self):
        print('我会开卡车')
# 司机类
class Drivers(object):

    # 司机会开公交车
    def driverBus(self):
        return Bus().run()

    # 司机会开卡车
    def driverTruck(self):
        return Truck().run()
在这里插入代码片

上面的代码,如果司机还会开自行车,摩托车,拖拉机,碰碰车等等等,我们是不是就会在司机类写很多函数。

我们修改一下上面司机类:

# 司机类
class Drivers(object):
    # 司机会
    def driverAll(self,car):
        return car.run()
test = Drivers()
test.driverAll(Truck())
test.driverAll(Bus())

这样我们就只要写一个函数,这个函数接受一个Car类型的变量
然后我们只要把类的实例对象传参数的方式传入,就可以了,这就是多态。
其实在Java里这种叫向上取整,原理一样

偷偷告诉你,这个解释骗骗小白还行,都是障眼法,这个函数不只能接受Car类型的变量,任意类的实例只要类里面有run方法就不会报错,且能运行。
有意思的是当我提出疑问时,Python怎么回答我这个问题呢:
Python:

  1. 对于静态语言(例如Java)来说,如果需要传入Car类型,则传入的对象必须是Car类型或者它的子类,否则,将无法调用run()方法。
  2. 对于Python这样的动态语言来说,则不一定需要传入Car类型。我们只需要保证传入的对象有一个run()方法就可以了:
    这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
  3. Python的“file-like object“就是一种鸭子类型。对真正的文件对象,它有一个read()方法,返回其内容。但是,许多对象,只要有read()方法,都被视为“file-like object“。许多函数接收的参数就是“file-like object“,你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。

对此我只能说:Python程序员和猪都要吃喝拉撒睡,所以在Python程序员眼中Python程序员都是猪。(开个玩笑,别打我!后面会说到鸭子类型的好处

获取对象信息

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

   type(123)

我们都知道123肯定是int类型,所以type(123)返回的就是int类型咯

判断是否属于int类型:

print(type(123) == int) #True

其他类型同理,不多说。

我们上面有稍微说到,子类的实例,既属于子类类型,也属于父类类型。
那么我们判断它属于什么类型,使用type()就很不方便
用isinstance()

a = Bus()
isinstance(a, Bus) #True
isinstance(a, Car) #True
isinstance(a, Drivers) #False
isinstance(a, Bus) and isinstance(a, Car) #True

那么上面我们被忽悠过去的多态,司机类可以改成:

class Drivers(object):
    # 司机会
    def driverAll(self, name):
        if isinstance(name,Car):
            return name.run()
        print('你传过来个啥玩意!')  

在这里插入图片描述

能用type()判断都可以用isinstance()判断,
而且isinstance()还可以判断是不是属于多种类型中的一种:

isinstance(123, (int,str,list,tuple)) #True

dir()

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

dir('ABC')

返回了[‘add’, ‘class’,…, ‘subclasshook’, ‘capitalize’, ‘casefold’,…, ‘zfill’]

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

len('ABC')
'ABC'.__len__()

我们自己写的类,如果自己写一个__len__()方法,也可以用len(类对象)

仅仅把属性和方法列出来是不够的,配合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', 18) # 设置一个属性'y'
hasattr(obj, 'y') # 有属性'y'吗?#True
getattr(obj, 'y') # 获取属性'y' #18

如果试图获取不存在的属性,会抛出AttributeError的错误,可以传入一个default参数,如果属性不存在,就返回默认值:

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

也可以获得对象的方法:

hasattr(obj, 'power') # 有属性'power'吗?True
n = getattr(obj, 'power') # 获取属性'power'并赋值到变量n
n() # 调用fn()与调用obj.power()是一样的  81

实例属性和类属性

class Student(object):
    name = 'Student'

s = Student()
print(s.name) #Student 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
print(Student.name) #Student 
s.name = 'Michael' #给实例绑定name属性
print(s.name)  #Michael 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
print(Student.name) #Student 但是类属性并未消失,用Student.name仍然可以访问
del s.name # 如果删除实例的name属性
print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
Student.gender = '公的'#给类绑定一个属性
print(s.gender) # '公的' 给类绑定一个新的属性,添加前的实例也可以拿到
a = Student() #创建一个新的实例
s.name = 'Michael'
print(a.name) #Student 说明给一个实例绑定属性,不会影响其他实例
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值