类和对象

类和对象的概念

面向对象编程最重要的概念就是类和对象,类集合了所有对象的静态特征和动态特征,是对象的蓝图和模板,有了类就可以创建对象。所以类是抽象的,对象是具体的。如人是一个类,你,我,他就是属于人这个类的三个具体的对象。
对象的4个特征:
1.一切皆为对象
2.每一个对象都是独一无二的
3.对象可以归属某个类
4.对象是由类创造出来的,是具体的

定义类和创建对象

  • 定义类

定义类需要做两件事:数据抽象和行为抽象
数据抽象:抽取对象的共同的静态特征,称为属性;
行为抽象:抽取对象的共同的动态特征,称为方法。
如定义一个类为学生,学生的属性包含:姓名、年龄等,学生的行为包含:上课、考试等。
定义类的格式:class 类名(类的父类),注意:类名的命名采用驼峰命名法,每个单词的首字母大写。如下:

class Student(object):  # object - Student是object的子类
    pass

定义属性:
用构造方法(构造器/构造子 - constructor)设定属性,如下:

    def __init__(self, name, age):  # 也可为属性设置默认值,方法和函数相同
        # 给对象绑定属性(name, age)
        self.-name = name
        # self.__name = name 在属性名称前面加2个下划线 - 把这个属性隐藏起来,理论上值不能被修改,但可以通过其他方法修改
        # self.name = name 
        self.-age = age

在创建对象时,就需要调用构造方法,给对象的属性赋值,所以调用构造方法不是直接使用方法的名字,而是直接使用类的名字。如下:

stu1 = Student('文文', 16)

定义方法:
在类里面的函数称为一种方法,方法是这个类生成的对象的行为。我们定义一个方法就代表对象可以接收这个消息。对象的方法的第一个参数都统一写成self,它代表了接收消息的对象。如下:

    def study(self, course):  # self代表正在接收消息的对象
        print('%s正在学习%s' % (self.name, course))

    def test(self):
        print('正在参加考试')

调用方法的格式 :对象.消息(参数) ,只传self后的参数,因为对象就是self。如下:

stu1.study('Python')
stu2.test()
  • 创建对象

调用构造方法创建对象,实际上调用的是Student类中的 _init_ 方法
创建对象的格式:对象名称 = 类名(属性值)

stu1 = Student('文文', 16)

实例

用面向对象的思维写程序的过程就是指给对象发一个消息,只要对象接收到这个消息,就会自动完成这个行为。要完成这个程序有3个步骤:
1.定义类
2.调用构造方法,创建对象
3.给对象发出消息:通过给对象发消息,让对象完成某些工作。给对象发消息其实就是指调用行为

模拟数字时钟
import time

# 定义类
class Clock(object):
 
    def __init__(self, hour=0, minute=0, second=0):
        self._hour = hour
        self._minute = minute
        self._second = second

    def run(self):
        """走字"""
        self._second += 1
        if self._second == 60:
            self._second = 0
            self._minute += 1
            if self._minute == 60:
                self._minute = 0
                self._hour += 1
                if self._hour == 24:
                    self._hour = 0

    def show(self):
        return '%02d : %02d : %02d' % (self._hour, self._minute, self._second)

    # 下面的方法可以获取对象的字符串表示形式
    # 当我们用print打印对象时会自动调用该方法
    # print(clock1)
    def __str__(self):
        return '%02d : %02d : %02d' % (self._hour, self._minute, self._second)


def main():
   # 创建对象
    clock1 = Clock(1, 4, 49)  # 若此处不给属性赋值,则使用默认值
    while True:
        # 调用行为
        print(clock1.show())
        time.sleep(1)
        # 调用行为
        clock1.run()


if __name__ == '__main__':
    main()

类的继承

继承是从已经有的类创造新类的过程,提供继承信息的称为父类 / 超类 / 基类,得到继承信息的称为子类/派生类/衍生类,通过继承我们可以将子类中的重复代码抽取到父类中,子类通过继承并复用这些代码来减少重复代码的编写,将来如果要维护子类的公共代码,只需要在父类中进行操作即可。

  • 子类继承一个父类

子类可以通过调用父类初始化的方法,继承父类的属性

# B继承A
class A(object):

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

class B(A):

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

子类可以继承父类的行为(方法),也可以对方法进行重写 / 覆盖 / 置换 / 覆写(override)。方法重写指的是子类在继承父类方法之后,对方法进行了重新实现,当我们给子类对象发送执行此方法的消息时,执行的是子类重新写过的方法。不同的子类可以实现不同重写的版本。下面举例说明。

class Person(object):

    def __init__(self, name, gender):
        self._name = name
        self._gender = gender

    def drink(self):
        print('%s正在喝水' % self._name)


class Teacher(Person):

    def __init__(self, name, gender, subject):
        super().__init__(name, gender)
        self._subject = subject

   # 老师类对drink方法进行了重写
    def drink(self):
        print('%s正在喝茶' % self._name)


class Student(Person):

    def __init__(self, name, gender, score):
        super().__init__(name, gender)
        self._score = score

# 学生类对drink方法进行了重写    
def drink(self):
        print('%s正在喝饮料' % self._name)


def main():
    t1 = Teacher('Luo', 'male', 'Python')
    s2 = Stident('shu', 'female', '98')
    t1.drink
    s1.drink


if __name__ == '__main__':
    main()

老师和学生两个类都对父类的drink方法进行了重写,并且是不同版本的重写。当给老师对象发drink消息时,老师执行的是喝茶的行为,给学生发送drink消息时,学生执行的时喝饮料的行为。

  • 子类继承多个父类

如果一个子类继承多个父类,在调用父类初始化方法时要用父类调用。
如果多个父类有同一个方法,子类在调用此方法时会默认调用继承时写在最前面的父类的方法。
举例:儿子继承了三个类,假设这三个类都有drink的方法,当儿子调用drink方法时,会自动调用Musicion的drink方法

class Son(Musicion, Father, Monk):

    def __init__(self, name, nickname, art_name):
        # 调用父类初始化的方法
        Father.__init__(self, name)
        Monk.__init__(self, nickname)
        Musicion.__init__(self, art_name)

def main():
   s = Son('洛洛', ‘智障禅师’, 'kiki' )
   s.drink

if __name__ == '__main__':
   main()

如果子类重写了一个方法,相当于把所有有此方法的父类都重写了。如果父类都有同一个行为,最好把父类设置为抽象类,把方法设置为抽象方法,要求子类必须实现.。下面举例说明。

from abc import ABCMeta, abstractmethod


class Father(object, metaclass=ABCMeta):

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

    @abstractmethod
    def drink(self):
        pass


class Musicion(object, metaclass=ABCMeta):

    def __init__(self, art_name):
        self._art_name = art_name

    @abstractmethod
    def drink(self):
        pass


class Monk(object):

    def __init__(self, nickname):
        self._nickname = nickname

    def eat_vagetable(self):
        print(self._nickname + '在吃斋')


class Son(Musicion, Father, Monk):

    # 继承多个类
    # 调用类的构造方法
    def __init__(self, name, nickname, art_name):
        Father.__init__(self, name)
        Monk.__init__(self, nickname)
        Musicion.__init__(self, art_name)

    # 在子类重写drink方法,相当于把所有有drink方法的父类都重写了
    def drink(self):
        print(self._name + '在喝果汁')


def main():
    son = Son('LI', '智障禅师', 'wen')
    # 此处drink方法调用的是Son重写的喝果汁的drink
    son.drink()
    son.eat_vagetable()


if __name__ == '__main__':
    main()


抽象类

Python没有从语言层面支持抽象类的概念,我们可以通过abc(abstract class)模块来制造抽象类的效果。在定义类的时候通过指定metaclass=ABCMeta可以将类声明为抽象类,抽象类是不能创建对象的,抽象类存在的意义是专门拿给其他类继承,abc模块中还有一个包装器abstractmethod,通过这个包装器可以将方法包装为抽象方法,要求子类必须重写该方法
注意:如果抽象类中没有抽象方法,抽象类还是可以创建对象

实例

用下面的实例来说明抽象类和方法重写

计算不同岗位员工的工资
# 公司内三种员工
# 部门经理 - 固定月薪15000元  程序员 - 150元/小时  销售员 - 1200 + 5%销售额
# 给员工信息,算出员工月薪
from abc import ABCMeta,abstractmethod


# 强制子类必须实现方法
class Employee(object, metaclass=ABCMeta):
    """员工"""

    def __init__(self, name):
        """
        初始化方法

        :param name: 姓名
        """
        self._name = name

    @property
    def name(self):
        return self._name

    # 抽象方法
    @abstractmethod
    def get_salary(self):
        """
        计算月薪

        :return: 月薪
        """
        pass


class Manger(Employee):
    """部门经理"""

    def get_salary(self):
        return 15000


class Programmer(Employee):

    def __init__(self, name):
        super().__init__(name)
        # 联系不是最紧密的属性
        # 更适合赋默认值
        # 用setter方法进行修改
        self._working_hour = 0

    @property
    def working_hour(self):
        return self._working_hour

    @working_hour.setter
    def working_hour(self, working_hour):
        self._working_hour = working_hour \
            if working_hour > 0 else 0

    def get_salary(self):
        return 150 * self._working_hour


class Salesman(Employee):

    def __init__(self, name):
        # super(Salesman, self) 若Salesman只继承了一个父类,括号内的内容可以省略
        super(Salesman, self).__init__(name)
        self._sales = 0

    @property
    def sales(self):
        return self._sales

    @sales.setter
    def sales(self, sales):
        self._sales = sales if sales > 0 else 0

    def get_salary(self):
        return 1200 + self._sales * 0.05


def main():
    emps = [
        Manger('刘备'), Programmer('zhu'),
        Manger('cao'), Salesman('gou'),
        Salesman('lv'), Programmer('zhang')
    ]
    for emp in emps:
        # isinstance识别对象的类型
        if isinstance(emp, Programmer):
            emp.working_hour= int(input('输入%s本月工作时间:' % emp.name))
        elif isinstance(emp, Salesman):
            emp.sales = int(input('输入%s本月销售额' % emp.name))
        # 同样是接收get_salary这个消息,但是不同的员工表现出了不同的行为
        # 因为三个子类都重写了get_salary方法,所以这个方法会表现出多态行为
        print('%s本月工资为:¥%.2f元' % (emp.name, emp.get_salary()))


if __name__ == '__main__':
    main()

方法的多态行为
如果一个父类同时被多个子类继承,而多个子类都重写了父类中的同一个方法,那么这个方法给予表现出了多态行为。上面的实例中的get_salary方法就表现出来多态行为。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,849评论 6 13
  • 类是一个重要的C#编程概念,它在一个单元内定义了表示和行为。类提供了面向对象编程和面向组件编程所需的语言支持,是创...
    CarlDonitz阅读 810评论 0 2
  • 终于到了该离开的时候了,这个冬天注定要比以往来的早一些。是时候下决定了,前路艰险,只身一人可能会走的更加坦然...
    修缘12阅读 310评论 0 0
  • 下班时分,手机嘀嘀声起,打开一看,原来是狂暴风雨预警。 应景似的,刚还明亮的天空,一不察觉就暗沉了下来。 远方的天...
    水静深流阅读 240评论 0 2
  • 晚上晃了一会儿,练了邵老师的肝筋导引和肩颈,又在垫子上滚了背,肩颈还真是一日不练就抽抽,练完舒服多了。 这几日,总...
    misang阅读 168评论 2 5