第二十七篇 类和对象相关知识

Posted victorm

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第二十七篇 类和对象相关知识相关的知识,希望对你有一定的参考价值。

类和对象

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‘]}

# 这其实就是通过实力引用类的属性,并直接操作了类的属性
非赋值方式通过实例修改类的属性

 














以上是关于第二十七篇 类和对象相关知识的主要内容,如果未能解决你的问题,请参考以下文章

python全栈开发基础第二十七篇IO模型

C++从青铜到王者第二十七篇:特殊类设计

第二十七篇使用MVVM布局页面

第二十七篇 人间正道

小刘同学的第二十七篇博文

STM32F429第二十七篇之DMA实验详解