面向对象二(类的创建和调用+类的属性和方法+特殊参数Self+初始化方法init)2020-11-24

1. 类

我们所说的同属一类,就隐藏着一个共识,就是同一类的事物具有共同的特征。比如我们都是中国人,中国人是一类人,具有一些共同点:黑眼睛、黑头发,黄皮肤、用筷子、会讲汉语。而这些共同点就是我区别于其他类的依据。这些特点如果细分的话可以分成两种:

  • 第一种是描述事物是怎样的,如黑头发,黑眼睛,黄皮肤
  • 第二种是描述事物能做什么,有哪些行为和作用。如吃饭用筷子,会讲汉语。
    在编程世界中我们管第一种特征叫属性,第二种特征叫方法
    Python中每个类都有自己的属性和方法,每个实例对象都可以调用这些属性和方法。

1.1 类的创建

我们先创建一个电脑类:

class Computer:
	screen=True
	def start(self):
		print('电脑正在开机中')

电脑类都有屏幕,属性screen为True。
开机的时候会显示:电脑正在开机中。
我们来具体看看创建类的语法:
在这里插入图片描述
实例方法的创建语句,和函数的定义语句很类似,唯一不同的是:实例方法中有个必须放在首位的参数self。
类名的首字母要大写,以便让我们轻松地辨认出“哦!这个是类!”
例题:创建一个中国人类,并创建一个属性和方法。

class Chinese:
	eye=dark
	def speak(self):
		print('母语讲中文')

这样我们就创建了一个简单的类。但是如果我运行代码,不会有任何结果,也不会报错。因为我们创建了类,但是还没有调用类。

1.2 类的调用

下面我们调用刚才创建的类:

class Chinese:
	eye=dark
	def speak(self):
		print('母语讲中文')
xiaoming=Chinese()
print(xiaoming.eye)
xiaoming.speak()

结果
在这里插入图片描述
结果我直接调用的时候出现了报错,说dark没有定义。那是因为dark没有用引号,当作变量处理了。

class Chinese:
	eye='dark'
	def speak(self):
		print('母语讲中文')
xiaoming=Chinese()
print(xiaoming.eye)
xiaoming.speak()

执行结果
在这里插入图片描述
调用的关键在第9行代码:xiaoming = Chinese()。这个过程叫作:类的实例化,即在某个类下创建一个实例对象。至此我们创建了一个可以调用所有所属类的属性和方法的实例,语法如下:

实例名=类名()
xiaoming=Chinese()

我们来试下打印实例类型和直接打印出实例,会是什么结果呢?

class Chinese:
	eye='dark'
	def speak(self):
		print('母语讲中文')
xiaoming=Chinese()
print(type(xiaoming)
print(xiaoming)

在这里插入图片描述

我们看到xiaoming属于Chinese类,xiaoming是一个Chinese类对象,对象的内存地址是:0x000001A3D45694C0
当实例my_computer一被创建出来,就可以调用类中的属性和方法。一句话概括就是:类有的实例都会有。

1.3 属性和方法的调用

语法:实例名.属性 和 实例名.方法
例题:

class Chinese:
	eye='dark'
	def speak(self):
		print('母语讲中文')
xiaoming=Chinese()
print(xiaoming.eye)
xiaoming.speak()

在这里插入图片描述
实例在调用类的属性的时候,解析器查找的顺序是先在本实例对象下去查找,如果没有再去所属类对象中查找,如果本实例对象中有该属性,就输出该属性,如果没有输出所属类对象的该属性名的属性值,如果还没有则会报错。
例题:

class Chinese:
	eye='dark'
	def speak(self):
		print('母语讲中文')
xiaoming=Chinese()
xiaogang=Chinese()
xiaogang.eye='双眼皮'
xiaohong=Chinese()
print(xiaoming.eye,xiaogang.eye,xiaohong.eye)

在这里插入图片描述
如果去我调用一个当前实例对象和该类对象都没有的属性就会报错,如:

class Chinese:
	eye='dark'
	def speak(self):
		print('母语讲中文')
xiaohong=Chinese()
print(xiaohong.hair)

在这里插入图片描述
如果当前实例和所属类中都有所要调用的属性呢?就会照着我们刚才所说的顺序解析器去查找,先在当前实例对象中查找,若有就调用,没有再去所属类对象属性中查找。若都有,遵循就近原则。
在这里插入图片描述
所属类都是黑头发,但是小红的是红头发,估计是染了,开个玩笑。如果要调用小红的头发颜色,当然是她自己的红头发了。如果不知道她是什么颜色的头发,那么才会调用所属类中的黑头发。
在这里插入图片描述

总结说明:

  • 类对象和实例对象都可以保存属性和方法
  • 如果一个属性或者方法是所有实例所共享的,则应保存在类对象中
  • 如果一个属性或者方法是某个实例所独有的,则应该保存在实例对象中
  • 一般情况下属性保存到实例对象中,因为属性都是独立拥有的居多
  • 一般情况下方法保存到类对象中
  • 归纳一下三个步骤就是:创建一个类 —— 类的实例化 —— 用实例调用类的属性和方法。

再啰嗦一句:类中创建的属性和方法可以被其所有的实例调用,而且,实例的数目在理论上是无限的。如上面的例子中,我就创建了xiaoming=Chinese,xiaohong=Chinese,xiaogang=Chinese三个实例。因此,类就像一个“实例工厂”,因其为所有实例提供了一套蓝图(即预先设定好有什么属性和方法)。

2. 特殊参数: self

在前面定义一个类的方法的时候,大家注意到后面括号里有一个参数self,这个参数定义的时候必不可少,然而调用的时候却必须忽略。这是为什么呢?下面我们慢慢讲解。

2.1 self参数的作用

正式揭秘特殊参数self的作用:self会接收实例化过程中传入的数据,当实例对象创建后,实例便会代替 self,在代码中运行。换言之,self 是所有实例的替身,“替身”是什么意思呢?我们来看一个例子。刚刚我们列举的类方法都只有一个self参数,实际上和一般函数一样,类的方法也可以设置多个参数:

class Chinese:
	name='Li Guanghui'
	def speak(self, someone):
		print(someone+'是中国人')
person1=Chinese()
print(person1.name)
person1.speak()

在这里插入图片描述

报错提示,speak()方法里少传了一个参数。我们传入一个人名。

class Chinese:
	name='Li Guanghui'
	def speak(self, someone):
		print(someone+'是中国人,说汉语。')
person1=Chinese()
print(person1.name)
person1.speak('Li Guanghui')

在这里插入图片描述
self调用时要忽略,‘Li Guanghui’传给参数someone
这样写虽然没错,但实际上是多此一举,因为只要在say方法内部调用类属性’Li Guanghui’,就可以实现同样的功能,不必重复传参。
怎么在方法内部调用类属性呢?你可能会想着这样写:

class Chinese:
	name='Li Guanghui'
	def speak(self):
		print(name+'是中国人,会说汉语。')
person1=Chinese()
person1.speak()

但这样写会报错。
在这里插入图片描述
person1是Chinese这个类的实例,不能访问到变量name。还记得我们刚说的,如果要在类的外部调用类属性,我们得先创建一个实例,再用实例名.属性的格式调用吗?那么如果想在类的内部调用类属性,而实例又还没创建之前,我们就需要有个变量先代替实例接收数据,这个变量就是参数self。
正确的写法应该是这样子的:

class Chinese:
	name='Li Guanghui'
	def speak(self):
		print(self.name+'是中国人,会说汉语。')
person1=Chinese()
person1.speak()

在这里插入图片描述

当最后一行代码运行时,实例person1会像参数一样传给self,替换掉self,第67行的self.name等价于person1.name。person1.name就相当于调用了类属性name(即’Li Guanghui’),然后跑完整个方法。
他的作用相对于(对照上面的代码看):

class Chinese:
	name='Li Guanghui'
	def speak(person1):
		print(person1.name+'是中国人,会说汉语。')
person1=Chinese()
person1.speak()

而这里不是self其实就相当于自己的,就是实例对象自己的参数,后面无论创建什么实例,self都会被实例名所替代。
在这里插入图片描述
可见,self的作用相当于先给实例占了个位置,等到实例创建好就“功成身退,退位让贤”。

2.2 self参数的进一步理解

下面我想通过一个例子加深对self参数的认识。
例题:定义一个类,实现不同的实例调用类属性的时候,有不同的属性。

class Chinese:
	hair='黑色'
	def speak(self):
		print('我的头发颜色是黑色')
xiaoming=Chinese()
xiaohong=Chinese()
xiaoming.speak()
xiaohong.speak()

如果代码如上去写的话输出的是这样的:
在这里插入图片描述
结果都是“我的头发颜色是黑色的”,而我想实现的是小名的头发是黑色的,小红的头发是红色的。
我想到用格式化字符串知识这样处理:

class Chinese:
	hair='黑色'
	def speak(self):
		print('我的头发颜色是%s'%hair)
xiaoming=Chinese()
xiaohong=Chinese()
xiaoming.speak()
xiaohong.speak()

在这里插入图片描述
结果报错了,说hair这个变量没有定义。我们记得在函数中,函数是可以读取外部定义的变量的,外部不能读取函数内部定义的变量。但是在类中是不行的。在类中是不能直接调用外部的变量的,必须用实例名.属性名的语法去调用。所以:

class Chinese:
	hair='黑色'
	def speak(self):
		print('我的头发颜色是%s'%xiaoming.hair)
xiaoming=Chinese()
xiaohong=Chinese()
xiaoming.speak()
xiaohong.speak()

在这里插入图片描述

这次没有报错,但全是黑色。于是我想到定义实例自己的属性:

class Chinese:
	hair='黑色'
	def speak(self):
		print('我的头发颜色是%s'%xiaoming.hair)
xiaoming=Chinese()
xiaoming.hair='黑色'
xiaohong=Chinese()
xiaohong.hair='红色'
xiaoming.speak()
xiaohong.speak()

在这里插入图片描述
但是问题又出现了,我传递参数的时候,只能一次写一个人的名字,要么xiaoming 要么xiaohong。有没有一个方法,当实例改变时,这个参数也跟着改变呢?这个时候我关注到self参数,它会不会满足我的期待呢?我用不同的类的实例调用,然后打印一下self。

class Chinese:
	hair='黑色'
	def speak(self):
		print(self)
		#print('我的头发颜色是%s'%xiaoming.hair)
xiaoming=Chinese()
#xiaoming.hair='黑色'
xiaohong=Chinese()
#xiaohong.hair='红色'
xiaoming.speak()
xiaohong.speak()

在这里插入图片描述
很惊喜的发现,当用不同的实例调用speak()方法的时候,self的内存地址是不一样的,这说明它时两个完全不同的对象。我们再进一步研究self和实例对象的关系。

class Chinese:
	hair='黑色'
	def speak(self):
		print(self)
xiaoming=Chinese()
print(xiaoming)

在这里插入图片描述
我们发现对象xiaoming的内存地址和self的内存地址是完全一样的,说明是同一个对象。那xiaohong调用的时候呢?

class Chinese:
	hair='黑色'
	def speak(self):
		print(self)
xiaoming=Chinese()
xiaohong=Chinese()
print(xiaoming)
xiaoming.speak()
print(xiaohong)
xiaohong.speak()

在这里插入图片描述
我们发现当实例对象不同的时候,self对象和实例对象保持一致。
通过研究我们发现,如果xiaoming调用self就是xiaoming,如果xiaohong调用self就是xiaohong。那么,我们就可以直接这样处理上面的问题:

class Chinese:
	hair='黑色'
	def speak(self):
		print('我的头发颜色是%s'%self.hair)
xiaoming=Chinese()
xiaoming.hair='黑色'
xiaohong=Chinese()
xiaohong.hair='红色'
xiaoming.speak()
xiaohong.speak()

在这里插入图片描述
这就是实现了我们的目的。此处应该有鲜花。
综上,所以我们说self代表的是类的实例本身,方便数据的流转。对此,我们需要记住两点:

  • 第一点:只要在类中用def创建方法时,就必须把第一个参数位置留给 self,并在调用方法时忽略它(不用给self传参)
  • 第二点:当在类的方法内部想调用类属性或其他方法时,就要采用self.属性名或self.方法名的格式。

好,self这个神奇的参数就讲解到这里。对初学者来说,确实不是那么容易理解,如果不熟悉,可以多多温习,做做课后练习巩固一下哦。

3. 初始化方法(构造函数)

定义初始化方法的格式是def init(self),是由init加左右两边的【双】下划线组成( initialize “初始化”的缩写)。
初始化方法的作用在于:当每个实例对象创建时,该方法内的代码无须调用就会自动运行。

3.1 初始化方法的第一种引入

例子:

class Chinese:
	def __init__(self):
		print('很高兴遇见你,我是初始化方法')
person1=Chinese()

在这里插入图片描述
是不是很神奇?我们只是创建了实例,还没有调用,初始化方法就自动执行了!
利用这个特性,在编写习惯上,我们会在初始化方法内部完成类属性的创建,为类属性设置初始值,这样类中的其他方法就能直接、随时调用。我们来看个例子:

class Chinese:
	def __init__(self):
		self.mouth=1
		self.eye=2
	def speak(self):
		print('我有%s张嘴,%s只眼'% (self.mouth,self.eye))
person_01=Chinese()
person_01.speak()

在这里插入图片描述
注意再初始化方法中创建属性的格式:

self.mouth=1
self.eye=2

变量中的self一定不能丢。
除了设置固定常量,初始化方法同样可以接收其他参数,让传入的这些数据能作为属性在类的方法之间流转。我们再来看个例子:

class Chinese:
	def __init__(self,name,birth,region):
		self.name=name
		self.birth=birth
		self.region=region
	def speak(self):
		print('我是%s,出生于%s年,我的出生地是%s'%(self.name,self.birth,self.region))
guanghui=Chinese('Guanghui',1982,'Henan')
guanghui.speak()

在这里插入图片描述
留心观察:
162行代码中,定义初始化方法的时候不仅有特殊参数,也有其他三个形参。
163-165行代码中,定义初始化变量,并把形参赋值给它们,这样传入的实参直接成为初始化变量的值。当然不一定是self.name=name,也可以self.abc=name,这个是任意的,只要后面传入变量是跟定义的一致就行了。
168行代码中,在类的实例化时记得一定要传入所需要的实参,不然会报错。
在这里插入图片描述
这里报错说缺少三个位置参数。
随着我们想实现的功能愈发复杂,我们会在类内部编写很多的方法,如果我们需要传入的数据能在类中长久保存并能被随时调用,初始化方法就是一个不错的解决方案。
练习:补充下面的代码以实现后面的打印结果是:你在哪里出生?我出生在河南。

class Chinese:
    def 

    
    def born(self):
        print('我出生在%s。' % self.hometown)

guanghui = Chinese('河南')
guanghui.born()

答案:

class Chinese:
    def __init__(self,hometown):
    	self.hometown=hometown   
    def born(self):
        print('我出生在%s。' % self.hometown)

guanghui = Chinese('河南')
guanghui.born()

在这里插入图片描述

3.2 初始化方法的另一种引入

例题:

class Chinese:
	name='葫芦娃'
	def speak(self):
		print('大家好,我是%s'%self.name)
p_01=Chinese()
p_01.speak()

在这里插入图片描述
现在我想多创建几个实例对象,而且不同的实例对象的名称不同。

class Chinese:
	# name='葫芦娃'
	def speak(self):
		print('大家好,我是%s'%self.name)
p_01=Chinese()
p_01.name='钢铁侠'
p_01.speak()
p_02=Chinese()
p_02.name='蜘蛛侠'
p_02.speak()
p_03=Chinese()
p_03.name='绿巨人'
p_03.speak()
p_04=Chinese()
p_04.name='黑寡妇'
p_04.speak()

输出结果

大家好,我是钢铁侠
大家好,我是蜘蛛侠
大家好,我是绿巨人
大家好,我是黑寡妇

这样来定义不同的属性不仅很麻烦,而且如果一个忘记定义,也不会提醒,就会导致执行时报错。
为此,我们可以用一种特殊的方法来解决问题。

class Chinese:
	def __init__(self,name):
		self.name=name  #这里的self.name不一定是这样写,可以写self.abc

	def speak(self):
		print('大家好,我是%s'%self.name)
p_01=Chinese('钢铁侠')
p_01.speak()
p_02=Chinese('蜘蛛侠')
p_02.speak()
p_03=Chinese('绿巨人')
p_03.speak()
p_04=Chinese('黑寡妇')
p_04.speak()

在这里插入图片描述
这中特殊方法的好处就是,如果实例化类对象,就必须传入所需要的实参,不传入的化会有报错提醒。而且,你不需要再一个一个的添加实例的属性了,直接当成参数传入,就会再初始化方法中被self.name接收成为属性值,实例一旦创建,初始化方法立即运行,就相当于直接创建了实例对象的属性。

4. 作业

4.1 写博客梳理知识点

4.2 敲课堂代码三遍起

猜你喜欢

转载自blog.csdn.net/m0_46738467/article/details/110082463