Python学习第六章:类和对象

6.1 类和对象

6.1.1 定义类

面向对象编程中,有两个关键名词,类和对象(实例),所谓类和对象,可以类比人类和个人,个人具备人类的共性,但特点上又存在区别。
Python定义一个类的语法格式如下:
class 类名:
执行语句
零到多个类的设置
零到多个方法

一般来说类名只要符合Python的语法规则即可,但实际操作中,Python的类型必须是由一个或者多个有意义的单词连缀而成的,并且每个单词的首字母大写,并且单词与单词之间不能采用任何分隔符!!!
类中包含有两个最重要的成员就是变量和方法。
类中各个成员之间的定义顺序任意,各成员之间可以相互调用

Python是一门动态性的语言,能够动态的增删变量,比如:对一个在类中没有定义的变量进行赋值,会自动添加一个新的类变量,需要删除则使用del,实例变量的增删道理上是一样的

在类中定义的方法默认是实例方法,其定义方式与函数的定义相同,只是实例方法的第一个参数会绑定调用者,通常用self代替。
实例方法中还有一个特别的方法,init(),也就是构造方法,构造方法一般用于构造该类的对象,通过使用构造方法来返回这个类的对象。
构造方法是创建这个类对象的根本途径,因此,如果开发者并没有对构造方法进行定义,那么系统会自动为这个类定义一个构造方法

class Person:
    '''this is a study case'''
    hair = 'Black'
    def __init__(self,name = 'pwc',age = 18):
        #add two instance for object
        self.name = name
        self.age = age
    def speak(self,content):
        print(content)

6.1.2 对象的产生和使用

与其他面向对象语言不同,Python创建一个对象不需要使用new来创建,

#直接使用上文中定义的class类
p =Person()
#此时p就是新创建的实例对象

具体使用p这个对象调用变量和方法:

p = Person()
print("我是:",p.name,"年龄:",p.age)
#访问p的name 直接给name赋值
p.name = 'dwj'

print(p.name)
p.speak("goubi")

至于name和age是何时定义的呢?

self.name = name
self.age = age

这两行代码实际上就是定义实例的过程,首先,Python第一个self参数绑定的其实是创建的实例对象,上文中提到过,Python是一门动态性语言,对实例对象的变量赋值,会添加这个参数,因此name和age就是这个时候定义的

6.1.3 对象的动态性

对象的动态性就是可以增删变量和方法,增删变量上文中提到过,这里来说一下增删方法,为对象动态增加方法,Python不会自动将调用者绑定到第一个参数,

def info(self):
    print("--niubi--")
#此时想要向p对象增加一个新的方法
p.foo=info
#由于Python不会自动绑定本身,所以需要手动添加
p.foo(p)

p.ads = lambda self : print('try again')
p.ads(p)

6.1.4 实例方法和自动绑定self

上面有提过略

6.2 方法

6.2.1 类调用实例方法

6.2.2 类方法与静态方法

Python中除了支持创建实例方法以为,还支持类方法,甚至支持静态方法,所谓的类方法和静态方法实际上就是说类无需创建一个实例对象就可以直接调用这个方法,区别在于,类方法会自动将这个类绑定在第一个参数上,而静态方法需要手动绑定。

class Bird:
    @classmethod
    def fly(cls):
        print('I can fly',cls)
    @staticmethod
    def eat(p):
        print("I can eat",p)
Bird.fly()
Bird.eat('meat')
b = Bird()
b.fly()
b.eat('car')

在这里插入图片描述

6.2.3 函数装饰器

def funA(fn):
    print('A')
    fn()
    return 'A的返回值'

@funA
def funB():
    print('B')
print(funB)

上面的函数意味着两步,首先funB将作为funA的参数被传入,其次funB将被替换为funA函数的返回值

6.2.4 再论类命名空间

6.3 成员变量

6.3.1 类变量和实例变量

在类命名空间内定义的变量就属于类变量,Python可以使用类来读取、修改变量

class Address:
    detail ='广州'
    post_code = '6666666'
    def info(self):
        print(Address.detail)
        print(Address.post_code)
print(Address.detail)
print(Address.post_code)

addr = Address()
addr.info()

Address.detail = 'liubi'
addr.info()

上述中detail和post_code作为类变量,只能依靠类进行调用,必须使用类名来调用

class Record:
    item ='niubi'
    date ='2019-12-12'
    def info(self):
        print('dayin',self.item)
        print(self.date)
rc = Record()
print(rc.date)
print(Record.date)
print(rc.info())

Python中允许使用对象来调用类变量,因为对象访问类变量的本质就是类直接调用类变量;
然而,如果使用对象来对类变量进行赋值则不一样了,

class Inventory:
    #定义两个变量
    item = '鼠标'
    price ='2000'
    def change(self,item,price):
        #这里做个解释,self.item是实例调用类变量
        self.item = item
        self.price = price

iv = Inventory()
iv.change('键盘',5000)
print(iv.item)
print(iv.price)

print(Inventory.item)
print(Inventory.price)

同样的道理,如果此时修改类变量的值,实例对象的变量值同样不会被修改

6.3.2 使用property函数定义属性

Python的property()函数是用来定义属性的方法,在使用的时候可以传入4个参数,分别代表:getter方法、setter方法、del方法、doc属性。其中doc是一段字符串属性,起到描述作用的,当然开发者可以向property()函数中传入不同的参数,可以传入0个代表不能读也不能写,传入1个代表只读,传入2个代表可读可写,传入3个代表可读可写可删除

class Rectangle:

    def __init__(self,width,height):
        self.width = width
        self.height = height

    def setsize(self,size):
        self.width,self.height = size

    def getsize(self):
        return self.width,self.height

    def delsize(self):
        self.width,self.height = 0,0

    size = property(getsize,setsize,delsize,'用于描述矩形大小的属性')

#首先访问doc属性
print(Rectangle.size.__doc__)
help(Rectangle.size)
rect =Rectangle(4,3)

print(rect.size)
rect.size = 9,7
print(rect.size)
print(rect.getsize)

还可以使用@property装饰器来修饰方法,使其成为属性

class Cell:
    @property
    def state(self):
        return self._state
    @state.setter
    def state(self,value):
        if 'alive' in value.lower():
            self.__state= 'alive'
        else:
            self.__state = 'dead'
    @property
    def is_dead(self):
        return not self.__state.lower() == 'alive'

c = Cell()
c.state = 'Alive'
print(c.state)
print(c.is_dead)

6.4 隐藏和封装

封装是面向对象的三大特征之一,还有两个是继承和多态,它指的是将对象的状态信息隐藏在对象的内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问
封装的用途主要有:

  • 隐藏类的实现细节
  • 让使用者只能够通过事先预定的方法来访问数据,从而可以在该方法里加入控制逻辑,限制对属性的不合理访问
  • 可进行数据检查,从而有利于保护对象信息的完整性
  • 便于修改

一般而言,为了实现良好的封装一般从两个方面来考虑
讲对象的属性和实现细节隐藏起来,不允许外部直接访问
将方法暴露出来,让方法控制对这些属性进行安全的访问
但是由于python中并不提供private之类的修饰符,所以Python并不能够真正的支持隐藏,因此,为了能够实现隐藏的效果,只要将python类的成员名命名以双下划线开头的,Python就会将他们隐藏起来

class User:
    def __hide(self):
        print('这是被隐藏了的方法')
    def getname(self):
        return self.__name
    def setname(self,name):
        if len(name)<3 or len(name)>8:
            raise ValueError('用户名的长度不对')
        self.__name = name
    name = property(getname,setname)
    def setage(self,age):
        if age < 18 or age > 70:
            raise ValueError('年龄不符合规定')
        self.__age = age
    def getage(self):
        return self.__age
    age = property(getage,setage)

U =User()
U.name = input('输入名字')
U.age = int(input('年龄'))
U._User__hide()

6.5 类的继承

继承是面向对象的三大特征之一,也是实现软件复用的重要手段,Python的继承是多继承机制,即一个子类可以继承多个父类

6.5.1 继承的语法

Python中子类继承父类的语法格式如下:
class SubClass(SuperClass1,SuperClass2,…)
另外如果定义一个类并没有显示的指明是继承自哪个类,那么默认为继承自Object类

class Fruit():
    def info(self):
        print('我是一个水果!重%d克'%self.weight)
class Food():
    def taste(self):
        print('不同食物的口感不同')
class Apple(Fruit,Food):
    pass

a = Apple()
a.weight = 600
a.info()
a.taste()

6.5.2 关于多继承

Python中虽然也支持多继承,但仍然尽量使用单继承,避免出现不必要的错误
因为当一个子类同时继承多个父类的时候,如果父类中的方法有重名的话,那么排在前面的父类方法会遮蔽排在后面的父类方法。此处也不做解释

6.5.3 重写父类的方法

class Bird():
    def fly(self):
        print('我是鸟我能非')
class chicken():
    def fly(self):
        print('虽然我也属于鸟,但我的飞有点特别')
a = chicken()
a.fly()

6.5.4 使用未绑定的方法调用被重写的方法

如果在程序中既要使用子类重写后的方法,又要用到父类的方法,此使就需要使用类名的直接调用类空间的方法,因为使用类名直接进行调用,并没有自动绑定对象上面,因此需要手动将对象绑定

class Base():
    def foo(self):
        print('我是基类')
class Sub():
    def foo(self):
        print('我是子类')
    def bar(self):
        print('我是bar')
        self.foo()
        Base.foo(self)
a = Sub()
a.foo()
a.bar()

在这里插入图片描述

6.5.5 使用super函数调用父类的构造方法

Python中子类会继承得到父类的构造方法,如果子类有多个直接父类,那么排在前面的父类构造方法会被优先调用。Python中要求:如果子类重写的了父类的构造方法,那么子类的构造方法必须调用父类的构造方法,主要有两种形式

  • 使用未绑定的方法
  • 使用super()函数来调用父类的构造方法

6.6 Python的动态性

6.6.1 动态属性与__slots__

为了防止一个类中由于动态特性的原因,导致为其添加了许多的变量和方法,使用__slots__能够限定允许更改的内容

6.6.2 使用type()函数定义类

6.6.3 使用metaclass

6.7 多态

6.7.1 多态性

class bird():
    def move(self,field):
        print('鸟在%s'%field)
class dog():
    def move(self,field):
        print('狗在%s'%field)
x = bird()
x.move('天空')
x= dog()
x.move('大地')

在这里插入图片描述

6.7.2 检查类型

python提供了两个函数来检查类型

  1. issubclass(cls,class or tuple):检查cls是否为后一个类或者元组保活的多个类中任意类的子类
  2. isinstance(obj,class or tuplr): 检查obj是否为后一个类或者元组包含的多个类中任意类的对象

6.8 枚举类

有些情况下,一个类的对象是有限且固定的,比如季节类,它只有四个对象,这种有限对象的类,成为枚举类
程序中提供了两种方式来定义枚举类:
直接使用enum列出多个枚举值类创建枚举类
通过继承Enum基类来派生出枚举类

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 点我我会动 设计师:白松林 返回首页