zoukankan      html  css  js  c++  java
  • 第二十七篇 类和对象相关知识

    类和对象

    1. 什么叫类:类是一种数据结构,就好比一个模型,该模型用来表述一类食物(食物即数据和动作的结合体),用它来生产真是的物体(实例)

    2. 什么叫对象:睁开眼,你看到的一切事物都是一个个的对象,你可以把对象理解为一个具体的事物(事物即数据和动作的结合体)

    (铅笔是对象,人是对象,房子是对象,狗是对象,你看到的都是类)

    3.类与对象的关系:对象都是由类产生的,女娲造人,首先由一个造人的模板,这个模板就是人类,然后女娲根据类的定义来生产一个个的人

    4. 什么叫实例化:由类生产对象的过程叫实例化,类实例化的结构就是一个对象,或者叫做一个实例(实例=对象)

    实例:就是类生产的那个对象。是一个实实在在的存在。放到真实世界中,人类就是一个类,这人类是看不见,摸不着的,是人为给划分的;而人,比如,你,我,他,路人等就是一个个的实例,是真实存在,能看得见,摸得着的。

    类相关的知识

    • 声明类

    在Python中,声明函数和声明类很相似:

    声明函数

    def function(args):
        '''文档字符串'''
        函数体

    声明类

    class 类名:
        '''类的文档字符串'''
        类体
    class Chinese:   # 声明类,类名的规范:类名的首字母要大写
        '''这是一个中国人的类'''
        pass
    
    print(Chinese) 
    # <class '__main__.Chinese'>
    声明类的定义示例
    class Chinese:   
        '''这是一个中国人的类'''
        pass
    
    # 实例化
    #实例化,对类名加括号,加括号就代表运行,运行类也有返回值,这个返回值就是一个具体的示例了
    p1=Chinese()
    print(p1)
    # <__main__.Chinese object at 0x00402EF0>
    
    #实例化到底干了什么?后面给你答案
    类的实例化

    经典类与新式类

    经典类:上面的两个示例就是经典类,经典类的特点就是,类名后面直接就是冒号(Python2中)

    新式类:都要在类名后面加个括号,括号里写上object

    # 新式类:都要在类名后面加个括号,括号里写上object
    # 意思是Chinese这类继承与object
    class Chinese(object):
        pass

    注意,Python3中统一都是新式类,没有区别了,类名后面是否加(object),都是新式类。

    • 属性

    类是用来描述一类事物的,类的对象指的是这一类事物中的一个个体。

    既然是事物,那就要有属性,属性分为:

    1. 数据属性:就是变量   (就是第二十五篇里讲的特征)

    2. 函数属性:就是函数,在面向对象里通常称为方法  (就是第二十五篇里讲的动作)

    注意:

    类和对象均用点来访问自己的属性

    • 类的属性

    数据属性即变量,类的定义与函数又极其类似,其实可以用函数的作用域来理解类的属性调用。

    class Chinese:
        '这是一个中国人的类'
        # 类的数据属性
        party='渣滓洞'
    
        # 类的函数属性,就是方法
        def huang_pi_fu():
            print('中国人都是黄皮肤')
        def cha_dui(self):
            print('%s插到了前面' %self)
    • 访问类的属性(查看类属性)

    要访问类的属性,你首先得知道类的属性在哪里吧?就好比你要逛亲戚,你得知道亲戚家在哪里吧,然后才能选择用什么交通工具到你亲戚家。

    那要去你亲戚家,你怎么知道路呢?可以问爸妈,也可以给亲戚打电话,还可以让亲戚发定位等等。

    同理,定义了类的属性,也要知道存放在哪里了?有两种方式可以查看

    #第一种:
    dir(类名): 得到的是一个名字列表,只放了属性的名字在列表里
    
    print(dir(Chinese))
    
    # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'cha_dui', 'huang_pi_fu', 'party']
    
    
    # 第二种:
    类名.__dict__ :查出的是一个字典,key为属性名,value为属性值
    __dict__: 查看类的属性字典
    print(Chinese.__dict__)
    
    # {'__module__': '__main__', '__doc__': '这是一个中国人的类', 'party': '渣滓洞', 'huang_pi_fu': <function Chinese.huang_pi_fu at 0x00C01AE0>, 'cha_dui': <function Chinese.cha_dui at 0x00C01A50>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>}

    知道了存放地点,就可以一级级查找下去,访问类的属性啦

    print(Chinese.__dict__['party'])   # 渣滓洞
    print(Chinese.__dict__['huang_pi_fu']())  # 中国人都是黄皮肤
    print(Chinese.__dict__['cha_dui']('汪汪队'))  # 汪汪队插到了前面

    Python提供了更简单的方法:

    类名.属性名:就可以访问到属性。(本质上就是在查询属性字典)

    # 访问类的数据属性,通过点来访问
    print(Chinese.party)    # 渣滓洞
    
    # 访问类的函数属性,通过点来访问
    Chinese.huang_pi_fu()    # 中国人都是黄皮肤
    
    Chinese.cha_dui('dig')   # dig插到了前面
    • 特殊的类属性

    #python为类内置的特殊属性
    类名.__name__# 类的名字(字符串)
    类名.__doc__# 类的文档字符串
    类名.__base__# 类的第一个父类(在讲继承时会讲)
    类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
    类名.__dict__# 类的字典属性
    类名.__module__# 类定义所在的模块
    类名.__class__# 实例对应的类(仅新式类中)
    
    类的特殊属性(了解即可)
    print(Chinese.__name__)       # Chinese
    print(Chinese.__doc__)        # 这是一个中国人的类
    print(Chinese.__base__)       # <class 'object'>
    print(Chinese.__bases__)      # (<class 'object'>,)
    print(Chinese.__dict__)        #  上面已经有了
    print(Chinese.__module__)        #__main__
    print(Chinese.__class__)        #<class 'type'>

    对象相关的知识

    对象:是由类实例化而来的。

    • 构造初始化方法及实例化
    class Chinese:
        '这是一个中国人的类'
    
        party='渣滓洞'
        '''
        首先,在类定义当中,要有一个初始化函数来帮我们定制每一个对象的属性
        其次,类class的语法结构当中,提供了内置方法,只要你把初始化函数定义为__init__()这个名字,你在用类名()来运行你的类的时候,他就会自动的找到
        __init__来帮你去运行
        第三,__init__()必须要有一个self参数,然后再接着写其他数据属性(变量参数)
        第四, 定义数据属性时,self.mingzi = name,代表给这个self这个实例赋予了一个mignzi属性,mingzi属性就是数据属性;name就是执行__init()函数传进来的参数
        这样就把名字,年龄,性别统统都封装到了self这个实例自己里面了
        最后,return返回了一个self实例,但是不用显示的return,而是class自动给你return了,最后返回的结果就是一个字典。
        
        字典里封装了什么?封装了名字,年龄,性别这些数据属性
        
        '''
      # 1. 初始化构造方法 def __init__(self,name,age,gender): print('我是初始化函数,我开始运行了') # self:就是实例自己 self.mingzi=name # 相当于 p1.mingzi=name self.nianji=age # 相当于 p1.nianji=age self.xingbie=gender # 相当于 p1.xingbie = gender print('我结束啦') # 上面已经构造好了初始化一个实例的方法,下面就来真真的生成一个实例对象 # 具体的实例化的过程: # 1. 实例化的过程,本质上就是调用并运行了一次__init__(self,name,age,gender)函数, # 2. 在调用__init__的过程中,self其实就是p1,在运行过程中,会自动的将p1传给self,然后'元昊',18,'female'分别传给name,age,gender # 3. 其实就是Chinese.__init__(p1,name,age,gender)
    # 2. 实例化:实际就是生成了一个真实存在的对象。
    p1 = Chinese('小黄',18,'female')
    # 我是初始化函数,我开始运行了
    # 我结束啦
    # 上面初始化构造方法里面已经讲了,初始化构造返回的实例就是一个数据字典,那我们来看看到底是不是 print(p1) # <__main__.Chinese object at 0x00C39890> print(p1.__dict__) # {'mingzi': '元昊', 'nianji': 18, 'xingbie': 'female'}
    • 实例调用属性
    # 上面已经实例化了一个p1,p1就是真实存在的一个人
    # 就可以查看p1这个人具备哪些属性?
    # 首先,他具有这个类所共有的属性,还可以有他自己特有的属性
    
    print(p1.mingzi)   # 小黄
    
    # p1.mingzi,作用域在__init__里,可以调用到,很正常
    # 那p1.dang首先在__init__作用域里没找到,就会向外一层找,就有dang这个属性啦,所以可以调用了
    
    print(p1.dang)     # 渣滓洞
    
    # 查看p1的属性
    print(p1.__dict__)
    # {'mingzi': '元昊', 'nianji': 18, 'xingbie': 'female'}
    
    # 查看类的属性
    print(Chinese.__dict__)
    # {'__module__': '__main__', '__doc__': '这是一个中国人的类', 'dang': '渣滓洞', '__init__': <function Chinese.__init__ at 0x00AB1B70>, 'sui_di_tu_tan': <function Chinese.sui_di_tu_tan at 0x00AB1B28>, 'cha_dui': <function Chinese.cha_dui at 0x00AB1AE0>, 'eat_food': <function Chinese.eat_food at 0x00AB1A50>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>}

    小总结:

    1. 实例是怎么产生的?

    【答】实例的产生,就是执行了一个初始化方法__init__()产生的。__init__()返回的就是一个实例属性字典。实例属性字典里就是没有函数属性,并不包含函数属性

    2. 函数属性是属于类的,从层级关系上也能看出来。

    • 构造函数属性
    class Chinese:
        '这是一个中国人的类'
    
        dang='渣滓洞'
        # 构造了初始化函数
        def __init__(self,name,age,gender):
            self.mingzi=name        # 相当于 p1.mingzi=name
            self.nianji=age         # 相当于 p1.nianji=age
            self.xingbie=gender     # 相当于 p1.xingbie = gender
    
        # 构造函数属性
        def sui_di_tu_tan(self):
            print('%s 朝着墙上就是一口痰' %self.mingzi)
        def cha_dui(self):
            print(self)
            print('%s 插到了前面' %self.mingzi)
    
        def eat_food(self,food):
            print('%s 正在吃%s' %(self.mingzi,food))
    • 实例调用函数属性
    # 实例调方法的顺序
    # 1. 先从自己的字典里找 p1自己的字典:{'mingzi': '元昊', 'nianji': 18, 'xingbie': 'female'}
    # 2. 再到类的字典里面去找, 类的字典:Chinese.__dict__
    # 能找到就可以运行
    
    p1.sui_di_tu_tan()
    # 小黄 朝着墙上就是一口痰
    # 问题:sui_di_tu_tan(self)定义的时候有个self参数,那为什么这里运行的时候,没有传参数,也能正常运行呢?
    • 关于self的总结

    1、self 就代表自己,自己就是实例化的结果p1。谁来实例化,self就是谁。
    2、为什么要有self?self就是来做统一的
    3、只要定义了self,一执行函数,就会自动把p1传给函数的第一个参数。
    4、所以,以后只要碰到self,就要知道self就是实例本身

    来一张图

     有人说,类有数据属性和函数属性,实例/对象 是由类产生的,所以实例也有数据属性和函数属性了------这是错的哇,记住啦。

    因为:

    实例化的过程实际就是执行__init__的过程,这个__init__函数内部只是为实例本身即self设定了一堆数据(变量),所以实例只有数据属性;它的所谓的函数属性是从类里找来的而已。

     还有人说,实例是类产生的,所以实例肯定能访问到类属性,然后就没有说为什么了------这也是错的哇。

    因为:

    1. 首先你会发现,实例化就是 类名(),然后返回的结果是一个对象,加上括号是不是跟函数运行很像,函数运行完了有返回值,很像那

    2. 函数有作用域的概念,其实类也有作用域的概念,二者一样

    3. 你可以吧class当做最外层的函数,是一个作用域

    # 定义一个类,只当一个作用域用
    
    class MyData:
        pass
    
    x = 10
    y = 20
    
    MyData.x = 1
    MyData.y = 2
    
    print(x, y)
    print(MyData.x, MyData.y)
    print(MyData.x + MyData.y)
    View Code

    4. 实例化会自动触发__init__函数的运行,最后返回一个值即实例,我们要找的实例属性就存放在__init__函数的局部作用域里

    5. 类有类的属性字典,就是类的作用域,实例有实例的属性字典,就是实例的作用域

    6. 综上,一个点代表一层作用域,obj.x 先从自己的作用域找,自己找不到再去外出的类的字典中找,都找不到,就会报错

    7. 在类中没有使用点的调用,代表调用全局变量。

    所以,上面的说法犯了因果倒置的错误。不是因为实例是由类产生的,所以实例才能访问到类属性。

    上面这个只是为了帮助理解,而不能真的这么说。

    茅塞顿开,总结一句话就是:

    类有个属性字典,实例也有个属性字典;
    查的时候,实例/对象先从自己的属性字典里查,再到类的属性字典里去查

    整理下上面的代码

    class Chinese:
        '这是一个中国人的类'
    
        dang='渣滓洞'
        def __init__(self,name,age,gender):
    
            self.mingzi=name        # 相当于 p1.mingzi=name
            self.nianji=age         
            self.xingbie=gender     
    
    
        def sui_di_tu_tan(self):
            print('%s 朝着墙上就是一口痰' %self.mingzi)
        def cha_dui(self):
            print(self)
            print('%s 插到了前面' %self.mingzi)
    
        def eat_food(self,food):
            print('%s 正在吃%s' %(self.mingzi,food))
    
    # 实例化可以生成多个对象
    p1=Chinese('小黄',18,'female')
    # 对象调用吃的方法
    p1.eat_food('包子')   # 小黄 正在吃包子
    
    
    p2=Chinese('武sir',23,'姑娘')
    # 对象也可以调用吃的方法
    p2.eat_food('韭菜馅饼')  武sir 正在吃韭菜馅饼
    • 属性有两种:数据属性和函数属性
    • 类属性的使用----增删改查
    class Chinese:
        country='China'
        def __init__(self,name):
            self.name=name
        # 函数属性的定义原则:动词_名词。 干什么事
        def play_ball(self,ball):
            print('%s 正在打 %s' %(self.name,ball))
    
    # 下面的函数定义与类是同级的,与类没半毛钱关系。
    def say_word(self,word): return "%s 说 %s" %(self.name, word) # 实例化一个实例 p1=Chinese('alex') # print(p1.__dict__) # {'name': 'alex'}
      •   查看类的属性
    #查看类的数据属性:用点的方式查。 
    print(Chinese.country)  # China
    
    # 查看类的函数属性
    print(Chinese.play_ball)  # <function Chinese.play_ball at 0x009F1AE0>
      •   增加类的属性
    #增加类的数据属性
    Chinese.dang = 'Gong Chandang'
    print(Chinese.dang)    # Gong Chandang
    
    # 增加类的函数属性
    # 首先,要在类的外部写一个函数
    def say_word(self,word):
        return "%s 说 %s" %(self.name, word)
    
    # 其次,增加函数属性,将上面的函数赋值给类的函数属性Say_Language
    Chinese.Say_Language = say_word
    print(Chinese.__dict__)
    # {'__module__': '__main__', 'country': 'China', '__init__': <function Chinese.__init__ at 0x00C01B28>, 'play_ball': <function Chinese.play_ball at 0x00C01AE0>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None, 'Say_Language': <function say_word at 0x00C01B70>}
      •    修改类的属性
    #修改类的数据属性
    Chinese.country="新西兰"
    print(Chinese.country)  # 新西兰
    
    # 类属性被修改了,那实例可以使用吗?可以用的
    print(p1.country) # 新西兰
    
    # 修改类的函数属性
    
    # 首先要定义个要修改的目标函数
    def play_what(self,what):
        return "%s 正在玩 %s" %(self.name, what)
    
    # 修改类的函数属性
    Chinese.play_ball = play_what
    print(Chinese.__dict__)
    # 调用类的函数属性
    print(p1.play_ball("钢琴"))   # alex 正在玩 钢琴
      •   删除类的属性
    # 删除类的数据属性
    del Chinese.country
    print(p1.country)
    # AttributeError: 'Chinese' object has no attribute 'country'
    
    
    # 删除类的函数属性
    
    del Chinese.play_ball
    print(Chinese.__dict__)
    # {'__module__': '__main__', 'country': 'China', '__init__': <function Chinese.__init__ at 0x01331B28>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None}
    
    
    # 调用类函数属性
    p1.play_ball("足球")
    # AttributeError: 'Chinese' object has no attribute 'play_ball'
    # 因为,已经被删除掉了
    • 实例属性的使用----增删改查
      •   查看实例的属性
    # 查看实例的数据属性  
    print(p1.name) # alex 
    
    # 查看实例类属性(查看实例的函数属性,实际上是访问的类的函数属性)
    print(p1.play_ball) # <bound method Chinese.play_ball of <__main__.Chinese object at 0x00D22ED0>>

    # 运行,函数属性后加()就变成运行了。
    print(p1.play_ball('篮球'))
      •   增加实例的属性
    # 增加实例的数据属性
    p1.age=18
    print(p1.__dict__)     # {'name': 'alex', 'age': 18}
    print(p1.age)          # 18
    
    #备注,无法增加实例的函数属性哦,因为函数属性是通过类增加的
    
    
    # 增加实例的函数属性(虽然可以增加,但是没有这么玩的,要知道可以增加,但要迅速忘记它,千万别这么用,纯属多余的)
    def shili_shuxing(self):
        return "我是来自实例的函数属性"
    p1.Shi_Li_De_Shu_Xing = shili_shuxing
    
    print(p1.__dict__)
    # {'name': 'alex', 'Shi_Li_De_Shu_Xing': <function shili_shuxing at 0x00D61AE0>}
    print(p1.Shi_Li_De_Shu_Xing(p1))
    # 我是来自实例的函数属性
      •   修改实例的属性
    # 修改实例的数据属性
    p1.age=19
    print(p1.__dict__)
    print(p1.age)
    
    # 结果
    {'name': 'alex', 'age': 19}
    19
      •    删除实例的属性
    #删除实例的数据属性
    del p1.age
    print(p1.__dict__)

    那,换个姿势,实例和类混合着玩会怎么样?

    # 示例代码
    
    class Chinese:
        country='China'
        def __init__(self,name):
            self.name=name
    
        def play_ball(self,ball):
            print('%s 正在打 %s' %(self.name,ball))
    
    
    p1=Chinese('alex')
    print(p1.country)  # China     访问的是类的
    
    
    # 在p1里新增了一个country
    p1.country='日本'
    print('类的--->',Chinese.country)   # China
    print('实例的',p1.country)          # 日本
    示例代码1
    # 作用域问题
    
    class Chinese:
        def __init__(self,name):
            self.name=name
    
        def play_ball(self,ball):
            print('%s 正在打 %s' %(self.name,ball))
    
    p1=Chinese('alex')
    print(p1.country)
    # AttributeError: 'Chinese' object has no attribute 'country'
    # 原因, 因为类里没有country属性
    
    # 那如果把country定义到类外面那?
    country='中国'
    class Chinese:
        def __init__(self,name):
            self.name=name
    
        def play_ball(self,ball):
            print('%s 正在打 %s' %(self.name,ball))
    
    p1=Chinese('alex')
    print(p1.country)
    # AttributeError: 'Chinese' object has no attribute 'country'
    # 原因:作用域只在类里面找,放到类外部照样找不到
    示例代码2
    class Chinese:
        def __init__(self):
         # 在实例化过程中接受输入参数
            name = input("请输入用户名: ")
            self.name=name
    
        def play_ball(self,ball):
            print('%s 正在打 %s' %(self.name,ball))
    
    p1=Chinese()
    print(p1.name)
    
    
    # 备注:
    '''
    虽然这么做是可以的,但是千万别这么干。
    因为一个函数就是实现一个功能,而这么干,还实现了接受用户输入的功能。
    可读性差,也不利于维护
    '''
    
    # 可以改进成如下方式,专门写一个函数来处理接受输入和实例化的过程,代码变为:
    
    class Chinese:
        def __init__(self, name):
            self.name=name
    
        def play_ball(self,ball):
            print('%s 正在打 %s' %(self.name,ball))
    
    def shi_li_hua():
        name=input('>>: ')  # 接收输入
        p1=Chinese(name)    # 实例化
        print(p1.country)   # 调用
        print(p1.name)      # 调用
    
    shi_li_hua()    # 直接调用运行
        
    千万不要这么干

    在类中没有使用点的调用,代表调用全局变量。

    country='中国'
    class Chinese:
        def __init__(self,name):
            self.name=name
            print('--->',country)
    
        def play_ball(self,ball):
            print('%s 正在打 %s' %(self.name,ball))
    
    
    p1=Chinese('alex')
    # ---> 中国
    
    # 为什么?
    # 其实前面已经给了答案了。
    所说的在类里面找属性,是通过点这种方式才表明这个属性是需要再类里面去找的,而此时,country并没有通过点的方式,所以它就是一个普通的变量名,即使这个变量在类的外面,照样也可以使用
    不通过点的方式也可以在类里使用变量
    country='-------------中国-------------'
    class Chinese:
        country='中国'
        def __init__(self,name):
            self.name=name
            print('--->',country)
    
        def play_ball(self,ball):
            print('%s 正在打 %s' %(self.name,ball))
    
    p1 = Chinese("alex")
    # ---> -------------中国-------------
    # 原因:
    # 遵循在类中没有使用点的调用,代表调用全局变量。
    遵循在类中没有使用点的调用,代表调用全局变量。没有通过点,根本不会从类里面找的
    class Chinese:
        country='China'
        def __init__(self,name):
            self.name=name
    
        def play_ball(self,ball):
            print('%s 正在打 %s' %(self.name,ball))
    
    
    p1=Chinese('alex')
    
    print(p1.country)   # China
    
    p1.country='Japan'   # 改的只是p1自己的国家
    print(Chinese.country)  # China    类的数据属性还是没有被改掉的
    会改掉类的属性吗?
    class Chinese:
        country='China'
        l=['a','b']
        def __init__(self,name):
            self.name=name
    
        def play_ball(self,ball):
            print('%s 正在打 %s' %(self.name,ball))
    
    p1=Chinese('alex')
    print(p1.l)    # ['a', 'b']
    
    p1.l=[1,2,3]   # 给p1新增了一个属性值,所以只会改p1自己的
    print(p1.l)    # [1, 2, 3]   改的仅仅是p1自己的
    print(Chinese.l)   # ['a', 'b']   类的属性l 是不会变的
    print(p1.__dict__)   # {'name': 'alex', 'l': [1, 2, 3]}
    
    
    # 但是,这种方式就有问题了
    # 首先,没有给p1新定义属性,
    # 其次, p1.l调的就是类的,这个l那的就是列表的引用,append操作的就是类
    p1.l.append('c')  
    print(p1.__dict__)  # {'name': 'alex', 'l': [1, 2, 3]}
    print(Chinese.l)  # {'name': 'alex', 'l': [1, 2, 3, 'c']}
    
    # 这其实就是通过实力引用类的属性,并直接操作了类的属性
    非赋值方式通过实例修改类的属性
  • 相关阅读:
    21 jsp——jsp中的相对路径和绝对路径
    20 jsp——jsp的九大内置对象
    19 jsp——jsp的转发标签
    18 jsp——静态引入和动态引入
    17 jsp——全局代码,局部代码
    【zabbix告警监控】配置zabbix监控nginx服务
    【nagios监控】基于linux搭建nagios监控
    【zabbix监控问题】记录zabbix控制面板报错及日志报错的解决方法
    【docker构建】基于docker构建rabbitmq消息队列管理服务
    【docker构建】基于docker搭建redis数据库缓存服务
  • 原文地址:https://www.cnblogs.com/victorm/p/9310761.html
Copyright © 2011-2022 走看看