奋斗小糖果
1. 封装
1.1 封装的引入
- (1) 定义封装(Encapsulation)
对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其含义是其他程序无法调用。
封装,就是将类或者是函数中的某些属性限制在某个区域之内,外部无法调用。 - (2)为什么使用分装
未封装属性 可以直接“对象.属性”来修改属性的值,这样导致不安全。
因此,需要一种方式来增强数据的安全性。即属性不能随意修改;属性不能修改成任意的值。Python通过封装的方式来解决该问题。
1.2 封装的方法
-
(1)封装是面向对象的三大特性之一。
指隐藏对象中一些不希望被外界所访问到的属性和方法。 -
(2)如何隐藏属性
将属性名修改成为外界不知道的名字。
将属性名修改为hidden_name
class dog:
def __init__(self,name):
self.hidden_name = name#隐藏属性name
def speak(self):
print('大家好,我是%s'%self.hidden_name)
d=dog('二哈')
d.speak()
大家好,我是二哈
上述案例如果外界直接使用实例化对象名.属性名修改属性,不能直接修改属性。
d.name='哈哈哈'
#不能修改上述案例中的属性。
- (3)隐藏属性的获取方法和修改方法
需要提供一个getter
和setter
方法,这两个方法是外部可以访问并修改的。
----- (1)
#getter语法
def getter_name(self):
#getter_name()这个方法用来获取对象的name属性
return self.hidden_name
案例-getter
class dog:
def __init__(self,name):
self.hidden_name = name#隐藏属性name
def speak(self):
print('大家好,我是%s'%self.hidden_name)
def get_name(self):
return self.hidden_name
d=dog('二哈')
print(d.get_name())
二哈
#setter 语法
get_name(self,name)
self.hidden_name=name
案例-setter
class dog:
def __init__(self,name):#初始化的init的时候,不要在————末尾加name
self.hidden_name=name
def speak(self):
print('大家好,我是%s'%self.hidden_name)
#修改属性
def set_name(self,name):#设置属性的时候需要在括号里面加name
self.hidden_name=name#等于name
def get_name(self):
return self.hidden_name
d= dog('二哈')
d.speak()
d.set_name('大狼狗')
d.speak()
大家好我是二哈
大家好我是大狼狗
- 封装说明:
使用封装,会增加类的定义的复杂度,但是它也确保了数据的安全性。
即:
(1)隐藏了属性,在调用时无法随意修改对象的属性。
(2)增加了getter
和setter
方法,较好的控制属性是否只读或修改。
(3)使用setter
的方法修改属性,增加数据的验证,确保数据值是正确的。例如可以在setter
里面增加条件判断,满足条件的才会被修改,不满足条件的不能修改。
(4)可以在读取和修改属性的时候做一些其他的处理。
getter
方法后面的括号里面没有name参数。
setter
方法后面的括号里面有name参数。
1.3 封装方法的补充
可以对对象的属性使用双下划线的方式__xxx
封装,
这种封装方式是python自动给属性起名字,外界不知道他的具体名字。
\一般情况下,用一个下划线就可以达到封装的目的_xxx
这个名字的样式是__类名__属性名
例如__person_name
class person:
def __init__(self,name):
self.__name=name#双下划线封装方法
def get_name(self):
return self.__name
def set _name(self,name):
self._name=name
2. 拓展
2.1 @property
- (1)
@property
是Python内置的@property装饰器就是负责把一个方法变成属性调用的。 - (2)可以防止属性被修改。
class person:
def __init__(self,name):
self._name = name
@property
def name(self):
print('get方法执行了')
return self._name
p=person('葫芦娃')
print(p.name)
- (2)
@属性名.setter
可以修改属性。
具体参考:
https://www.liaoxuefeng.com/wiki/897692888725344/923030547069856
3. 类的继承
继承是面向对象的三大特征之一。
作用:
继承提高了代码的复用性;
让类与类之间产生了关系,有了关系之后才有了后面的多态特性;
- (1)类的继承语法:
在定义子类的时候,可以在类名后的括号中指定当前的父类(超类,基类)。
class Animals:
def run(self):
print('动物会跑')
def sleep(self):
print('动物会叫')
class dog(Animals):#dog类继承了Animals类的,将Animals放于dog类的括号中
def see_home(self):
print('狗看家')
d=dog()
d.see_home()
d.run()
d.sleep()
狗看家
动物会跑
动物会叫
-
(2)在创建类的时候,如果省略了父类,则默认父类是object。object是所有类的父类。
-
(3)检查一个对象是否另一个类的实例。
使用isinstance()
函数检查
class Animals:
def run(self):
print('动物会跑')
def sleep(self):
print('动物会叫')
class dog(Animals):
def see_home(self):
print('狗看家')
r=isinstance(d,Animals)#检查一个对象是否另一个类的实例
print(r)
True
- (4)检查一个类是否是另一个类的子类
使用issubclass()
函数
class Animals:
def run(self):
print('动物会跑')
def sleep(self):
print('动物会叫')
class dog(Animals):
def see_home(self):
print('狗看家')
r1=issubclass(dog,Animals)#检查一个类是否是另一个类的子类
print(r1)
True
- (5)子类方法的添加和父类方法的重写
如果子类的方法与父类的方法重名时,则通过子类调用方法时,会调用子类的方法,而不是父类的方法,这个特点称为方法的重写(或者覆盖)。如果父类中的方法子类不够用,则可以在子类中添加新的方法。
注意:
当我们调用一个对象的方法时:
会优先去当前对象中寻找是否具有该方法,如果有则直接调用;
如果没有,则去当前对象的父类中寻找,如果父类中有则直接调用父类中的方法;
如果没有,则去父类中的父类寻找,以此类推,直到找到object,如果依然没有找到就报错了。
3.1 多重继承
多重继承就是一个儿子(子类)有多个爹(父类)。
调用函数__bases__
可以获取当前类的所有父类,使用语法:子类名.__bases__
class Animals:
def run(self):
print('动物会跑')
def sleep(self):
print('动物会叫')
class dog(Animals):
def see_home(self):
print('狗看家')
print(dog.__bases__)#获取dog类的所有父类
(<class '__main__.Animals'>,)#dog的父类是Animals,该结果是个元组,所有可以有多个父类
注意:
如果多个父类中有同名的方法,则会优先在第一个父类中寻找,然后在第二个中找,依次类推。即前面的会把后面的覆盖。
4. 多态
多态也是面向对象的三大特征之一。
即 一个对象可以有多个不同的形态呈现。
- 多态案例
class A:
def __init__(self,name):
self._name = name
@property#getter方法装饰器
def name(self):
return self._name
@name.setter#setter方法装饰器
def name(self,name):
self._name = name
class B:
def __init__(self,name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class C:
pass
a = A('光辉')
a2 = A('Sam')
b = B('晓雯')
c = C()
def speak(o):
print('你好,我是%s'%o.name)
speak(a)
speak(a2)
speak(b)
speak(c)
你好,我是光辉
你好,我是Sam
你好,我是晓雯
AttributeError: 'C' object has no attribute 'name'#对speak()这个函数,只要对象中有name属性,他就可以作为参数传递,该函数不会考虑对象的类型,只要有这个现象就是一个多态的体现
- 非多态案例
在speak2()中,我们做了一个判断,即只有o满足是A类型的对象时,才可以正常调用,而其他类型的对象没办法使用该函数,这个函数就违反了多态。
class A:
def __init__(self,name):
self._name = name
@property#getter方法装饰器
def name(self):
return self._name
@name.setter#setter方法装饰器
def name(self,name):
self._name = name
class B:
def __init__(self,name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class C:
pass
a = A('光辉')
a2 = A('Sam')
b = B('晓雯')
c = C()
def speak2(o):
#判断
if isinstance(o,A):
print('你好,我是%s'%o.name)
speak2(a2)
你好,我是Sam
5. 总结面向对象的三大特性
- 封装:确保数据的安全
- 继承:保证了对象的扩展性
- 多态 :保证了程序的灵活性
6. 属性和方法
6.1 类属性和实例属性
- (1)类属性
直接在类中定义的属性就是类属性;
类属性的特点:类属性可以通过类和类的实例访问
类属性只能通过类对象去修改,无法通过实例对象去修改。
- (2) 实例属性
通过实例对象添加的属性称之为实例属性。
实例属性的特点:实例属性只能通过实例对象来访问,无法通过类对象访问。
class B:
count=0#类属性
def __init__(self):#实例属性,self为谁调用我就是谁,若a实例对象调用则为a.name=name#该形式为实例对象添加属性。
self.name=name
a=B()
6.2 类方法 和 实例方法
- (1) 实例方法
实例方法是在类中直接定义的;
在类中直接定义的,以self为第一个参数的都是实例方法。
当通过实例对象去调用方法时,会将当前对象作为self传入;
当通过类调用实例方法时,不会自动传递self,需要手动传入实例对象。
- (2) 类方法
定义类方法的时候需要使用@classmethod
装饰器。
类方法的第一个参数是cls,它也会自动传递,cls就是当前的类对象。
特点:类方法可以通过类对象和类的实例对象访问
@classmethod
def test2(cls):#括号中自动传入cls
print('我是test2方法,我是一个类方法')
6.3 类的静态方法
使用装饰器@staticmethod
.
静态方法基本是和当前类无关的方法,只是保存在当前类的一个函数;一般是一些工具方法,与当前类无关。
@staticmethod
def test3():#括号中没有参数
print('我是test3,我是静态方法')