Python面向对象

今天要学习的是面向对象,Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的,我们先一起来看一下类和对象的定义~

类和对象的定义

class 类的名称:
    语句块

# 举例说明
class Student:
    num = 100
    def showNum(self):
        return 200

print(Student.num)       # 输出:100
print(Student.showNum)
# 输出:<function Student.showNum at 0x009A9468>

# Student就是类对象,num是类变量,showNum是方法,self为类对象的实例, 类名称一般需要大写
  • :用来描述具有相同的属性和方法的对象的集合;

  • 对象:通过类定义的数据结构实例;

  • 简单来说就是在python中,用属性表示特征,用方法表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个;

实例的定义

讲完类和对象,我们来看一下实例,类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的属性可能不同;

class Student:
    def prt(self):
        print(self)
        print(self.__class__)

s = Student()
s.prt()

# 执行结果:
# <__main__.Student object at 0x0000000001D7CDA0>
# <class '__main__.Student'>

上述例子中self代表的是类的实例,而self.__class__指向类,且self 不是 python 关键字,我们把self换成其它的单词也可以正常运行;

什么是实例化

我们知道了什么是实例,那实例化又是什么呢,接着看下面这个例子,在Student类中有两个方法,一个是初始化方法__init__,另一个是我自己定义的方法showClass()方法

class Student:
    num = 100
    def __init__(self):
        self.name = '张三'
    def show(self):
        return '李四'

print(Student.num)         # 输出:100
print(Student.show)
输出:<function Student.show at 0x0000000002765BF8>

stu = Student()
# 这就是实例化,且实例化会自定调用__init__方法,self会自动传递,不能有return返回值

print(stu.name)            # 张三

  • 类名加括号就是实例化,实例化会自动调用__init__()方法,可以用它来为每个实例定制自己的特征(属性);

  • init()方法被称为类的构造函数或初始化方法,需要注意的是__init__()方法不能有return返回值;

类变量和实例变量

然后我们来讲一下什么是类变量和实例变量,看看这两者之间有什么不同;

# 实例变量是实例特有的,类变量是类和实例共有的
class Student:
    num = 100
    def __init__(self, name):
        self.name = name

    def showClass(self):
        return 200

print(Student.num)
print(Student.showClass)

stu = Student('张三') # 实例化,会调用__init__方法,self会自动传递,不能有return返回值

print(stu.name) # 实例变量
print(stu.num) # 类变量

直接看这个实例可能还不太直观,大家自己在程序里面动手运行一下,使用Student类调用num和name两个变量,就会发现在调用num的时候能正常输出,而调用name的时候报错了。然后再用实例对象stu调用这两个变量试试,我们就能得出下面两个结论:

  • 类变量:类变量在整个实例化的对象中是公用的,也就是定义在类中且在函数体之外的变量(但类变量通常不作为实例变量使用);

  • 实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,是定义在类里面的方法中的变量;

类方法和实例方法

讲完了类变量和实例变量,那我们来看一下类方法和实例方法:

class Student:
    num = 100
    def __init__(self, name):
        self.name = name
    def showClass(self):
        return '张三'

    @classmethod
    def add(cls):
        print(cls)

stu = Student('张三')    # 实例化,会调用__init__方法,self会自动传递,不能有return返回值
print(stu.name)          # 实例变量 输出:张三
print(stu.num)           # 类变量  输出: 100

print(stu.__dict__)     # 类的属性保存在自己的字典中,包括类变量和方法
print(stu.__dict__)     # 实例的属性保存在自己的字典中,包括实例的变量

stu.add()              # 类方法可以被实例对象调用  输出:<class '__main__.Student'>
stu.__class__.add()

print(Student.showClass())     # 报错,实例方法不可以被类对象调用
print(Student.name)            # 实例可以访问类的属性,类无法访问实例的属性

我们动手试一下,分别用Student类,和实例对象stu来调用类方法和实例方法,我们就能得出一下结论:

  • 类方法使用装饰器@classmethod,第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);

  • 类方法可以被实例对象和类对象调用;

  • 实例方法第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法);

  • 实例方法只能由实例对象调用;

静态方法

除了类方法和实例方法之外,还有一个方法叫做静态方法,我们一起来看一下静态方法如何使用:

class Student:
    def __init__(self, name):
        self.name = name

    @staticmethod
    def sub():
        print('static')

stu = Student('李四')

stu.sub()
# 实例可以调用静态方法    输出:static

Student.sub()
# 类可以调用静态方法  输出:static

然后我们可以得出一下两个结论:

  • 静态方法使用装饰器@staticmethod,参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;

  • 静态方法可以被实例对象和类对象调用;

私有属性和保护属性

  • 两个下划线__开头的属性为私有属性,不能在类的外部被使用或直接访问;

  • 一个下划线_开头的为保护属性,只有类实例和子类实例能访问,需通过类提供的接口进行访问;

我们来看一下私有属性和保护属性是如何使用的:

class Student(object):
    def __init__(self):
        self.__private_field = 200    # private
        self._protect_field = 200    # protect

stu = Student()
# print(stu.__private_field)  # 报错,实例对象不可以调用私有属性
print(stu._protect_field)   # 输出:200

属性装饰器

  • 第一种写法:使用@property装饰器,将类的方法变为属性;

class Student:
    num = 100
    def __init__(self, name):
        self.__name = name

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

    @name.setter
    def name(self, name):
        self.__name = name

    @name.deleter
    def name(self):
        del self.__name

stu = Student('张三')
print(stu.name)     # 输出:张三

stu.name = '李四'
print(stu.name)     # 输出:李四 

  • 第二种写法:使用属性函数property(),直接把方法当属性来操作;
class Student:
    def __init__(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

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

    def del_name(self):
        del self.__name
        print('实例的属性被删除了')

    # 这里表示property是用来修饰name这个属性的
    name = property(fget=get_name, fset=set_name, fdel=del_name, doc='hello')

stu = Student('张三')
print(stu.name)    # 张三

stu.name = '李四'  # 给name赋值
print(stu.name)   # 李四
  • 一个property对象包含三个方法:getter, setter, deleter,当一个函数被@property装饰器修饰时,系统会自动创建一个包含对应访问函数的同名属性;

类的继承

class Animal:
    def __init__(self):
        self.type = 'animals'
    def eat(self):
        print('{} 吃,Animal类中'.format(self.__class__.__name__))   # Animal 吃,Animal类中

class Person(Animal):
    def talk(self):
        print('讲话')

# animal = Animal()
# animal.eat()
# 
# print(animal.type)  # animals
# animal.talk()       # 报错,父类不可以调用子类的方法

person = Person()
print(person.type)  # animals

# Person调用Animal类的eat方法
person.eat()       # 输出:Person 吃,Animal类中
person.talk()      # 讲话
  • 继承是一种创建类的方法,一个类可以继承来自一个或多个父类,原始类称为基类或超类;

  • 继承可以很方便的帮助子类拥有父类的属性和方法,减少代码冗余,子类可以定义自己的方法和属性,也可以覆盖父类的方法和属性;

  • 实现继承:指使用基类的属性和方法而无需额外编码的能力;

  • 接口继承:指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构父类方法);

属性查找顺序

class Animal:
    __name = 'animal'
    def __init__(self):
        self.type = 'animal'
    def eat(self):
        print('{} eat'.format(self.__class__.__name__))

class Person(Animal):
    def talk(self):
        print('talk')

person = Person()
print(person.__name)   # 子类不能访问 父类的私有属性

  • 父类的私有属性无法被子类访问;

  • 属性的查找顺序:先从对象自身的__dict__中查找->然后从对象所在类的__dict__中查找->然后从父类的__dict__中查找,直至找到或者报错没有找到;

方法重写

  • 子类可以覆盖父类的方法,可以在覆盖的方法中调用父类的方法,且父类的类方法,静态方法也可以被覆盖
class Animal:
    def __init__(self):
        self.type = 'animal'

    def eat(self):
        print('{} eat'.format(self.__class__.__name__))

class Person(Animal):
    def eat(self):
        print('洗手')  # 洗手
        print('{} eat'.format(self.__class__.__name__))  # Person eat
        super().eat()  # 调用父类的方法                    # Person eat
        # super(Person, self).eat()   等价于 super().eat()

person = Person()
person.eat()
  • 继承中的init方法
class Animal:
    def __init__(self):
        self.one = 'one'

class Person(Animal):
    def __init__(self):
        self.two = 'two'
        self.three = 'three'

    def show(self):
        print(self.one, self.two, self.three)

person = Person()
print(person.__dict__)  # 输出:{'two': 'two', 'three': 'three'}
person.show()    # 报错,person没有one这个属性
print(person.one)  #同样报错
  • 代码修改,如果父类有init方法,且子类也有init方法,最好在子类的init方法中手动调用父类的init方法
class Animal:
    def __init__(self):
        self.one = 'one'

class Person(Animal):
    def __init__(self):
        self.two = 'two'
        self.three = 'three'
        super().__init__()  # 继承父类的__init__方法,且父类init方法的调用写在子类的什么地方也有讲究

    def show(self):
        print(self.one, self.two, self.three)

person = Person()
print(person.__dict__)
print(person.one)
# 输出:one  ,因为继承了父类的init__方法,所以可以访问one属性
  • 如果你的父类方法的功能不能满足你的需求,就可以在子类重写你父类的方法;

  • 子类可以覆盖父类的方法,且可以在覆盖的方法中调用父类的方法;

  • super()函数是用于调用父类(超类)的一个方法, Python 3 可以使用直接使用super().xxx 代替 super(Class, self).xxx

装饰器

  • 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等;

  • python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象(函数的指针);

  • 概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能,也称之为扩展功能;

Mixin

  • Mixin是一种设计模式,通过多继承的方式对类的功能进行增强;

  • Mixin可以在不修改任何源代码的情况下,对已有类进行扩展;

  • 可以根据需要使用已有的功能进行组合,来实现“新”类;

  • 还能很好的避免类继承的局限性,因为新的业务需要可能就需要创建新的子类;

Mixin类的注意点

  • 在Mixin类中,不能写初始化的__init__方法,因为Mixin类不做为独立类使用;

  • Mixin类原则上必须作为其他类的基类,实现其他类的功能增强;

  • Mixin类的基类必须也是Mixin类;

  • 使用Mixin方式增强功能的的类,必须将Mixin类写在继承列表的第一个位置;

  • Mixin比decorator更加强大;

参考:https://www.9xkd.com/user/plan-view.html?id=1853344716

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

推荐阅读更多精彩内容

  • 1.1.类和对象 1.1.1.万物皆对象 分类是人们认识世界的一个很自然的过程,在日常生活中会不自觉地将对象进行分...
    Rolle_Wang阅读 717评论 0 0
  • 自己以前整理的笔记,不太完整,后续会不断更新。。。。 [ ] __new__方法扩展 [ ] 魔法方法 [ ] 什...
    alan2yang阅读 1,701评论 0 1
  • 本节课纲 类和对象 类的定义 self参数 初始化方法init() _str_()方法 面向对象vs面向过程 私有...
    郭_扬阅读 4,927评论 0 7
  • @[toc] 1. 面向过程和面向函数编程 1.1 面向过程编程 ② 面向函数编程 1.2 面向对象中的类和对象 ...
    Erics1996阅读 184评论 0 0
  • 最近扁桃体总是发炎,疼痛难忍,扁桃体肿大甚至化脓以及发烧,而且无意间摸到脖子上三四个结节,很是担心癌细胞转移了。 ...
    2个女儿的麻麻阅读 149评论 1 0