Python_base20181226_面向对象程序设计

类与对象的概念:

类:类比于人类
对象:类比于某个具体的人
类与对象的关系: 类是对象的模板
什么叫实例化:照着葫芦画瓢(根据类模板创建一个对象的过程就叫做实例化)
类的定义与实例化:

class Person: # 类定义pass'''
class Person(object): # 经典类定义pass
'''p = Person() # 实例化

类中的东西分为属性和方法。

注意,受老男孩的荼毒,把属性和方法都称之为属性。一个是数据属性,一个是函数属性。

class Person: # 类定义teian = 'cry' # 我是属性passp = Person() # 实例化
print(p.teian)

怎么查看类的属性?两种方法

dir(类名) # 查出来的是一个名字列表
类名.__dict__ # 查出来的是一个字典,key为属性名,value为属性值

类的定义等示例:

class Person: # 类定义teian = 'cry'def fangfa():print('我是方法')passp = Person() # 实例化
print(dir(Person))
print(Person.__dict__)
print(Person.teian) # 调用类的属性
Person.__dict__['fangfa']() # 调用类方法的手段之一输出:
['__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__', 'fangfa', 'teian']
{'__module__': '__main__', 'teian': 'cry', 'fangfa': , '__dict__': , '__weakref__': , '__doc__': None}
cry
我是方法

构造函数

构造函数是用作初始化某个实例用的。
一定要注意,某个类中,方法副本只有一个,属性副本由实例自己管理,
这样可以避免空间的浪费。
由于这种特性,所以实例化出来的对象可以访问类的属性,但是类访问不到
实例化出来的对象的属性。

    def __init__(self): # 我是构造函数self.teian = 'wawawa'self.wanna = None

完整代码:

class Person: # 类定义'''这是一个测试类'''teian = 'cry'def __init__(self): # 我是构造函数self.teian = 'wawawa'self.wanna = Nonedef fangfa():print('我是方法')pass
p = Person()
print(Person.__dict__) # 属性和方法们
print(Person.__doc__) # 文档字符串
print(Person.__base__) # 父类
print(Person.__bases__) # 父类们
print(Person.__module__)
print(Person.__class__)
print(Person.teian)
print(p.teian)
# 实例只有数据 没有方法
# 实例.__dict__中只有数据
# 实例能够访问类属性
# 类却访问不了实例属性
#print(Person.wanna) # 类访问不了实例属性的
print(p.wanna)输出:
{'__module__': '__main__', '__doc__': '\n    这是一个测试类\n    ', 'teian': 'cry', '__init__': , 'fangfa': , '__dict__': , '__weakref__': }这是一个测试类
(,)
__main__

cry
wawawa
None

类属性的增删改查

class Person:name = 'vth'pass# 查看
print(Person.name)
p = Person()
print(p.name)# 修改
Person.name = 'faker'
print(Person.name)
print(p.name)# 增加
Person.age = 11
print(Person.age)
print(p.age)# 删除
del Person.age
print(Person.__dict__)  输出:
vth
vth
faker
faker
11
11
{'__module__': '__main__', 'name': 'faker', '__dict__': , '__weakref__': , '__doc__': None}

实例属性的增删改查

class Chinese:country = 'China'def __init__(self,name):self.name = namedef play_ball(self,ball):print('%s 正在打 %s' %(self.name,ball))p1 = Chinese('alex')
print(p1.__dict__) # 方法是属于类的,对象只是能够调用而已 {'name': 'alex'}
# 也就是说,self.什么,什么就属于对象,否则就属于类
print(Chinese.__dict__) # self.name属于对象,所以类的dict中没有# 实例就是在调类的方法(优化)
# 实例只应该有属性,而不应该有方法。# 增加
p1.age = 18
print(p1.age)# 修改
p1.age = 19
print(p1.age)# 删除
del p1.age
print(p1.__dict__)输出:
{'name': 'alex'}
{'__module__': '__main__', 'country': 'China', '__init__': , 'play_ball': , '__dict__': , '__weakref__': , '__doc__': None}
18
19
{'name': 'alex'}

python类的小tips:
python的类可以当作C语言的结构体来使用(不定义方法就可以了)

class JGT:val = 0lenth = 0j = JGT()
j.val = 2
j.lenth += 1
print(j.val,j.lenth)
输出:
2 1

想访问在类中赋值的属性,只有两种访问方法:

类名.属性
对象名.属性

所以如果用的是传统的访问方法,访问的其实是一个变量,而不是类中的属性。

对象.属性 是先去自己的__dict__去找,找不到再去类的__dict__去找。
如果要给对象的某个属性进行赋值操作,这跟类没有任何关系,也不会将改动更新到类中。但是,如果对象拿到的是类中的某容器,而且作的不是赋值操作而是修改(调用方法,非赋值方式),那么改动会更新到类中。
注意如果是复制的话,相当于把obj.xxx指向了另外一个对象,跟原来类中的类属性已经没有关系了。

class Demo:l = [1,2]def __init__(self):k = 1d = Demo()
d.l.append(1) # d.l 拿到了类中列表l的引用
# 所以对列表l的操作就会被更新了
# 跟浅拷贝差不多
print(d.__dict__)
print(Demo.__dict__)输出:
{}
{'__module__': '__main__', 'l': [1, 2, 1], '__init__': , '__dict__': , '__weakref__': , '__doc__': None}

类中的方法可以访问全局的变量。

country = 0class China(object):def __init__(self):self.c = countryprint(country)passc = China()

输出结果:

0

静态属性

什么叫静态属性?说的就是数据属性。
@property装饰器的作用就是把方法变成一个属性。

class SusheMember:def __init__(self,name,space,attri):self.name = nameself.space = spaceself.attri = attridef getName(self):return self.namedef getAttri(self):return self.attridef getSpace(self):return self.space# property装饰器使对象有能力把一个方法当作属性来使用@propertydef getspace(self):return self.space
s = SusheMember('北京林业大学','海淀区','211')
print(s.getSpace(),s.getspace)

输出:

海淀区 海淀区

类方法

需求:只是类直接调用方法,跟实例没有关系时的处理方式。
@classmethod装饰器可以做到这一点。

class SusheMember:def __init__(self,name,space,attri):self.name = nameself.space = spaceself.attri = attridef getName(self):return self.namedef getAttri(self):return self.attridef getSpace(self):print(self)return self.space# property装饰器使对象有能力把一个方法当作属性来使用@propertydef getspace(self):return self.space@classmethoddef leifangfa(cls):print(cls)print('我是类方法')
s = SusheMember('北京林业大学','海淀区','211')
print(s.getSpace(),s.getspace)
SusheMember.leifangfa() # 直接调用类方法
# 其实和cls和self差不多。只不过self代表的是调用方法的对象,
# 而cls代表的是调用类方法的类。

结果:

<__main__.SusheMember object at 0x000001B77D7DC198>
海淀区 海淀区

我是类方法

静态方法

装饰器@staticmethod可以把一个方法变成静态方法。
staticmethod被称为类的工具包,它既可以被类使用,也可以被对象使用。

country = 0class China(object):k = 1def __init__(self):self.c = countryself.k = China.kprint(country)@staticmethoddef province():print('i love program')
c = China()
c.province()
China.province()

结果:

0
i love program
i love program

类的组合

何谓类的组合?顾名思义,就是很多类组合在一起形成的组合类。
比如人是一个类,胳膊也是一个类,头也是一个类,身体也是一个类,腿也是一个类。
人这个类是由胳膊、头、腿、身体等组成的。这就是组合。
代码实例:

class Haidian:pass
class Changping:pass
class Dongcheng:pass
class Xicheng:pass
class Chaoyang:pass
class Beijing:def __init__(self):self.haidian = Haidian()self.changping = Changping()self.dongcheng = Dongcheng()self.xicheng = Xicheng()self.chaoyang = Chaoyang()beijing = Beijing()
print(beijing.__dict__)

那么,组合类有啥用呢?
类和类之间是有关联的,所以要组合起来。其实就是把一部分对象当作另一部分对象的数据属性。
下面这个例子,海淀区有北京林业大学,清华大学,北京大学这几所学校。
而北京市有海淀区,昌平区等区。
所以要查找北京市昌平区有哪些学校,直接就可以查。

class Haidian:def __init__(self,name,schools):self.name = nameself.schools = schoolspass
class Changping:def __init__(self,name,schools):self.name = nameself.schools = schoolspass
class Dongcheng:def __init__(self,name,schools):self.name = nameself.schools = schoolspass
class Xicheng:def __init__(self,name,schools):self.name = nameself.schools = schoolspass
class Chaoyang:def __init__(self,name,schools):self.name = nameself.schools = schoolspass
class Beijing:def __init__(self,haidian,changping,dongcheng,xicheng,chaoyang):self.haidian = haidianself.changping = changpingself.dongcheng = dongchengself.xicheng = xichengself.chaoyang = chaoyang
class Schools:def __init__(self,name):self.name = name
bjfu = Schools('北京林业大学')
pku = Schools('北京大学')
thu = Schools('清华大学')
cpnormal = Schools('昌平师专')
dcmedicine = Schools('东城区医疗学校')
xcnormal = Schools('西城师专')
chaoyangpapapa = Schools('优衣库试衣间学校')
haidian = Haidian('海淀区',[bjfu,pku,thu])
changping = Changping('昌平区',[cpnormal])
dongcheng = Dongcheng('东城区',[dcmedicine])
xicheng = Xicheng('西城区',[xcnormal])
chaoyang = Chaoyang('朝阳区',[chaoyangpapapa])
beijing = Beijing(haidian,changping,dongcheng,xicheng,chaoyang)
for i in beijing.haidian.schools:print(i.name)

结果:

北京林业大学
北京大学
清华大学

继承

子类可以继承到父类的函数属性和数据属性。

class Father:passclass Mother:passclass Son(Father): # 单继承passclass Son1(Father,Mother): # 多继承pass

证据:

class Father:money = 99999999def __init__(self):self.money = 100000000def chifan(self):print('吃饭')
class Mother:home = '北京西山一号院联排别墅'def __init__(self):self.home = "北京朝阳联排别墅"def heshui(self):print('喝水')class Son(Father): # 单继承passclass Son1(Father,Mother): # 多继承pass
son = Son1()
print(son.money,son.home)
son.chifan()
son.heshui()
print(Son1.__dict__)
print(Father.__dict__)
print(Mother.__dict__)

结果:

100000000 北京西山一号院联排别墅
吃饭
喝水
{'__module__': '__main__', '__doc__': None}
{'__module__': '__main__', 'money': 99999999, '__init__': , 'chifan': , '__dict__': , '__weakref__': , '__doc__': None}
{'__module__': '__main__', 'home': '北京西山一号院联排别墅', '__init__': , 'heshui': , '__dict__': , '__weakref__': , '__doc__': None}

程序不仅表明了子类可以继承父类的方法和属性,而且它本身的__dict__里是没有这些方法和属性的,换句话说,调用的是父类的东西。
另外有个小彩蛋。这里我们用的是多继承。我们会发现,在Father和Mother中,分别设置了类属性money和实例属性money,还有类属性北京西山一号院联排别墅,和实例属性北京朝阳联排别墅。有趣的是,我们发现子类继承到的money是Father中的实例属性,而继承到的home是Mother类中的类属性。(如果Mother中没有定义home类属性,那么会报找不到home错误)–这个待会儿再说,先了解到这儿。

对子类进行实例化时,会自动调用父类的__init__方法。

class Father:def __init__(self,name):self.name = nameprint('卧槽,谁调用我?')def chifan(self):print('吃饭真香')class Son(Father):passs = Son('wth')print(Son.__dict__)
print(s.__dict__)

结果:

卧槽,谁调用我?
{'__module__': '__main__', '__doc__': None}
{'name': 'wth'}

结果分析:
因为self跟实例绑定的,所以实例化出了son,它在实例化时自动调用了父类的__init__方法,所以它就有了属性name,值为wth

继承的使用场景:

当类之间显著不同,并且较小的类是较大的类的组件的时候,用组合。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好。

继承的两种含义:
1、继承基类的方法,并且做出自己的改变或扩展(代码重用)
2、声明某个子类,兼容于某个基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法。

所谓第二个含义,就是说,比如我们说,所有的动物都要会吃饭喝水,那么别管是猫还是狗,都要会吃饭喝水。这样我们就可以定义一个动物类,定义出吃饭和喝水的方法,但是我们并不具体实现这个方法。具体实现的工作由具体子类来做。
但是,原生python没有任何手段可以要求子类必须实现接口类中的方法。先看一个例子:

class Animal:def eat(self):passdef drink(self):passclass Dog(Animal):passclass Cat(Animal):passs = Dog()

结果


我们可以看出,子类中没有实现父类中的方法,也能跑通代码。这可咋办啊?难不成成了君主立宪制的皇室,说话都没人听了???这样可不行。
python当然是有办法的,通过引入一个名为abc的模块来实现。没错就是abc,你没看错。然后接口类要继承自元类接口类(metaclass=abc.ABCMeta). 最后要给接口类中的方法加上装饰器@abc.abstractmethod,看名字就可以看出来,当作Java的抽象类处理即可。这样做的话,子类当中就必须实现这个方法,不然就没法实例化。

import abc
class Animal(metaclass=abc.ABCMeta):@abc.abstractmethoddef eat(self):pass@abc.abstractmethoddef drink(self):passclass Dog(Animal):passclass Cat(Animal):passs = Dog()

结果:

Traceback (most recent call last):File "D:/pythonprogram/class_demo/demo1.py", line 18, in s = Dog()
TypeError: Can't instantiate abstract class Dog with abstract methods drink, eat

接口继承实际上是做出了一个抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象。即对于猫和狗拥有的功能,不用去看猫类和狗类,直接去看动物类它们具有什么方法即可。这在程序设计上就叫做归一化。

继承的顺序

首先, 说一下,python中有新式类和经典类之分。python3中全部都是新式类。
其实新式类和经典类是python2中的概念。

class Demo:# 我是经典类pass
class D(Demo): # 我是经典类passclass DDDDDD(object): # 我是新式类pass
class AAAA(DDDDDD): # 我是新式类pass

也就是说,直接或者间接的继承自object时,为新式类。否则为经典类。
新式类的继承顺序体现在类的MRO列表中。
举个例子:继承关系如图所示:
在这里插入图片描述
F的继承顺序就是DBECA。首先,多继承时,按照先左后右的顺序找,这没错。所以F先找D,然后再找B。往后怎么找?不会找A的,为啥呢?因为A是左右两支的汇聚处。
也就是说,对于每个节点来说,要把它下面的节点按照从下到上,从左到右的顺序找遍了,才能找到本节点。
解释:F是多继承,所以肯定先找D。按照从下到上的顺序,D的上级B发现所有的子分支(其实就一个D)都找完了,所以可以找到本节点。然后D的上级B发现自己所有的子分支也都找完了,所以可以找到B。然后节点A发现自己的左分支找完了,但是右分支还没找,所以继续找右分支。
在这里插入图片描述
同理,如果F有两个子类G和H,而I继承自G和H,那么类I的继承顺序就是 GHFDBECA.

那么你可能会问了,如果是继承3个以上的呢?也没问题,只要你记住我们的原则:
I继承了3个子类,我们要找3支汇聚的地方才能汇聚后向上走(也就是说对于每一个节点要找完自己的所有分支才能往上走)。而C有两个子类,它需要等分支全部汇聚完成之后才能往上走。
在这里插入图片描述
解释:
I找了G,G找了F,F发现H还没走,于是先走H,然后再找F,F此时发现都找完了,往上走,F找D,D找B,B找A,A发现还有没找完的,于是从E开始,E找C,此时C发现还有没找完的,也就是Three,所以先找到Three,后C,最后A。

class A:def test(self):print('A')
class B(A):def test(self):print('B')
class C(A):def test(self):print('C')
class D(B):def test(self):print('D')
class E(C):def test(self):print('E')
class F(D,E):def test(self):print('F')
class G(F):pass
class H(F):pass
class Three(C):pass
class I(G,H,Three):passf = F()
print(F.mro())
print(I.__mro__)

结果:

[, , , , , , ]
(, , , , , , , , , , )

注意,所有的最顶端的类最后都会在mro列表中给出继承自object。
有人管这个寻找方式叫做广度优先。不准确,但是也凑合。

怎么看mro列表呢?
方法有两种

  • print(类名.__mro__)
  • print(类名.mro())

经典类中的继承顺序,是一条道走到黑(一直往上走到不能再走),然后换条路继续一条路跑到黑。
所以对于图1的经典类的继承顺序应该是FDBAEC

子类调用父类的方法

方式一:用类调用的方式来解决:
注意,用此种方法记得要传进去self.因为self代表的是调用此方法的对象调用时默认为该对象,不用传self;直接用类名调用的话,需要传self。
示例代码:

class Father:def __init__(self,name,age):self.name = nameself.age = agedef wanna(self):print('wanna')class Son(Father):def __init__(self,name,age,degree):Father.__init__(self,name,age)self.degree = degreedef wanna_cry(self):Father.wanna(self)print('wanna Cry')print('my degree is %s' %self.degree)s = Son('wth',21,'master')
s.wanna_cry()

输出:

wanna
wanna Cry
my degree is master

可见我们成功的调用了父类中的方法。
但是对于牛B的python来讲,这么玩还是有点low的。

方式二:用super
super()直接就代表了父类,而且不用自己传self参数。
示例代码:

class Father:def __init__(self,name,age):self.name = nameself.age = agedef wanna(self):print('wanna')class Son(Father):def __init__(self,name,age,degree):#Father.__init__(self,name,age)#super().__init__(name,age)#super(Father,self).__init__(name,age)super(__class__,self).__init__(name,age)#以上四句是等价的self.degree = degreedef wanna_cry(self):Father.wanna(self)print('wanna Cry')print('my degree is %s' %self.degree)s = Son('wth',21,'master')
s.wanna_cry()

结果:

wanna
wanna Cry
my degree is master

面向对象练习:

选课系统需求:
角色:学校、学员、课程、讲师
要求:

  • 创建北京上海两所学校
  • 创建Linux,go,python 3个课程,linux and python在北京开,go在上海开。
  • 课程包含:周期,价格。 通过学校创建课程
  • 通过学校创建班级。班级关联课程和讲师。
  • 创建学员时,选择学校,关联班级。
  • 创建讲师角色时要关联学校。
  • 以上所有的对象都要pickle序列化保存到文件中(还没做,明天补充)

关键点:关联时用组合。
所谓的关联就是,比如讲师。创建讲师时关联学校的意思是,实例化出来一个教师的时候,他所属的学校就确定是哪一所了。
示例代码:

class School:def __init__(self,name):self.name = nameself.courses = []self.classes = []def create_course(self,name,period,price,place): # 通过学校创建课程c = Course(name,period,price,place)self.courses.append(c)return cdef create_class(self,name,course,teacher): # 通过学校创建班级clas = Class(name,course,teacher)self.classes.append(clas)return clas
class Class:def __init__(self,name,course,teacher): # 班级关联课程和讲师self.name = nameself.course = courseself.teacher = teacher
class Course:def __init__(self,name,period,price,place): # 课程包括周期和价格self.name = nameself.period = periodself.price = priceself.place = place
class Student:def __init__(self,name,age,school,classes): # 创建学员时关联班级self.name = nameself.age = ageself.school = schoolself.classes = classes
class Teacher:def __init__(self,name,school): # 创建讲师时关联学校self.name = nameself.school = schoolschool1 = School('北京校区')
school2 = School('上海校区')
python = school1.create_course('python','半年','1元','北京校区')
linux = school1.create_course('linux','大半年','2元','北京校区')
go = school2.create_course('go','半年','1.5元','上海校区')
wupeiqi = Teacher('吴沛琪',school1)
python1class = school1.create_class('python1班',python,wupeiqi)print(wupeiqi.name,wupeiqi.school.name)
print(python1class.name,python1class.course.name,python1class.teacher.name)
print(school1.courses[0].name)

输出:

吴沛琪 北京校区
python1班 python 吴沛琪
python

多态:

什么是多态?多态大概就是,不知道变量指向的是哪个对象,也能对其进行操作。且操作的行为随着对象所属的类而异。
下面我们来说一下多态的作用。
假如你做了一个获取价格的函数。 第一位程序员,把价格放在了元组的第二个元素;你写了个if…然后这个程序员离职了,又来了个程序员,他把价格放在了字典的’price’中,你又写了个if。。。。。。所以,你确定现在考虑到了所有的可能性了么?假如又有个人要用列表存储价格呢?或者再来个人,不用’price’存储价格了,用’cost’呢???难不成写if写到地老天荒嘛? 如果你写好了这个模块,卖给了别人,也就是说你不能对这种情况更新了,那么有了新的需求,又怎么办呢? 所以这种实现不同行为的方式即不灵活也不切实际。

那么该咋办呢? 让对象自己去处理这种操作。对于一个变量x,你无需知道它是字符串还是列表就能调用方法count:只要你向这个方法提供一个字符作为参数,它就能正常运行。
反正虚头巴脑的,没看懂啥意思。

def get_price(object):if isinstance(object,tuple):return object[1]elif isinstance(object,dict):return int(object['price'])else:return 'xxx'

类的封装

python在语言层面没有对封装做任何限制。它只是建议使用者不能这么干。
示例代码:

class Fengzhuang:_vth = 'vth'__school = 'bjfu'def __init__(self):passf = Fengzhuang()
print(f._vth)
print(Fengzhuang.__dict__)
#print(f.__school) # 双下划线这样访问不到
print(f._Fengzhuang__school) # 但是在内部双下划线的属性是被改成了这样。

输出:

vth
{'__module__': '__main__', '_vth': 'vth', '_Fengzhuang__school': 'bjfu', '__init__': , '__dict__': , '__weakref__': , '__doc__': None}
bjfu

单下划线是建议不要在类外使用,但是在类外硬要使用python也没办法。
但是双下划线在类外不能直接使用,要用_类名__属性,如果不这么用,就不能用。

封装的意义在于防止外界直接动自己的属性。定义访问接口函数有利于执行正确的逻辑。
啥意思呢?比如一个人,实例化出来,他的财产很低,才10W. 他直接修改salary为1000000,这样显然是不被允许的,所以要在接口函数中加以限制:

class Person:def __init__(self):self.__salary = 10000def get_salary(self):return self.__salarydef set_salary(self,new_salary):if new_salary < 20000:self.__salary = new_salary
p = Person()
print(p.get_salary())
p._Person__salary = 100000000 #这样是不被允许的
print(p.get_salary())
p.set_salary(10009)
print(p.get_salary())
p.set_salary(20000)
print(p.get_salary())

输出:

10000
100000000
10009
10009

注意,_开头的变量可以被继承,__开头的直接不能被继承。

class Person:__name = "vth"_school = 'bjfu'def __init__(self):self.__salary = 10000def get_salary(self):return self.__salarydef set_salary(self,new_salary):if new_salary < 20000:self.__salary = new_salaryclass P(Person):pass
ori_p = Person()
p = P()
print(ori_p._school)
print(p._school)
#print(p._P__name) # 会报错,因为根本就继承不到
print(ori_p._Person__name)

输出:

bjfu
bjfu
vth

补充导入包知识点:

比如目录结构是这样的。
在这里插入图片描述
demo7中定义了两个函数,一个是test1,一个是_test2.
demo7中的代码:

def test1():print('test1')def _test2():print('test2')_test3 = 1

demo8中的代码:

from demo7 import test1,_test2,_test3
test1()
_test2()
print(_test3)

如果是用以上代码,那么两个函数都可以执行,没问题。
但是如果是这样的代码:

#from demo7 import test1,_test2from demo7 import *
test1()
_test2()

那么_test2()是不能用的。
结果:

Traceback (most recent call last):
test1File "D:/pythonprogram/class_demo/demo8.py", line 5, in _test2()
NameError: name '_test2' is not defined

注意这点,以单下划线开头的变量或者函数,在直接from demo7 import *时,是不能被导入的。
但是可以被from demo7 import xxx来导入。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部