面向对象基础笔记
面向过程
面向对象
类的初步理解
__init__方法的理解
区分类的属性与对象的属性
继承:派生 组合
抽象类
多态与多态性
封装: 类和对象的隐藏属性
面向过程
核心是过程,即解决问题的步骤,比如设计一条流水线, 是机械的思维方式。
- 优点: 将复杂的问题流程化,进而简单化;
- 缺点: 可扩展性差;
面向对象
- 对象就是属性与方法的结合体.
- 优点: 可扩展性强;
- 缺点: 编程复杂度高;
- 应用场景:应对客户不停变化的需求,尤其是互联网应用,游戏和企业内部的应用。
类的初步理解
类是一系列具有相似特征与方法的对象的集合。
在现实世界中,是先有对象,然后才有类;在程序中,一定是先有类,将类实例化后才有对象。
在类定义完成的那一刻会直接生成一个类的内部命名空间,而函数是只有被调用时才会生产内部命名空间。
- 类的属性
- 1.数据属性:类的数据属性是共享的,占用同一个内存地址, 无论是对象访问还是类本身访问都是相同的内幕才能地址(不包括__init__创建的特有数据属性);
- 2.函数属性:是绑定给类的实例化对象的使用的,绑定到不同的对象是不同的方法,对象调用绑定方法时候,会把对象当做第一个参数传入,但是类调用函数的时候访问的是另外一个函数内存地址,且不会主动将自己作为第一个参数传入进去;
- 类的用途:
- 1.类可以通过‘.’访问类的数据属性或者函数属性,并且可以对齐进行增加、删除、修改数据属性操作;
- 2.类名加上( )就直接实例化了一个类对象
__init__方法的理解
- 对象可以定制自己独有的特征,是类实例化对象的局部命名空间,可以通过类名.__dict__访问
- 类实例化后若检测到此放回会自动调用此方法
- 类的公共的属性和方法放在类里边,相当于类的命名空间,
具体的实例化的步骤:
- 先产生一个空对象 jack = People()
- 然后调用__init__方法,jack对象和参数都传进__init__(self,name, age, gender),对obj绑定对象数据属性,self.Name = name.....
- 可以通过jack,.__dict__方法访问类的命名空间,里面就有Name,Age,Gender等自己属性和值了
区分类的属性与对象的属性
类的属性是可以共享的,对象和类都可以访问
但是通过__ini__定义的属性是对象独有的,只有对象可以访问,类是不能访问的,也可以直接用对象.
赋值属性, 若查询不到属性,会直接向上在对象的类的命名空间查找,类有父类,会继续上溯父类的命名空间查找
继承
- 理解: 类与类之间的关系,类a继承类b,那么可以理解为类a是类b。
- python中支持多继承
- 作用:代码重用,减少代码重复
python2:
- 经典类:没有继承object的类以及它的子类都称为经典类,深度优先查找
- 新式类:与经典类相反,广度优先查找
python3默认都是新式类,都是继承object类
派生
在子类派生出的方法属性中重用父类的方法,有两种实现方式:
- 指名道姓,不依赖于继承
- super(),依赖于继承关系,在当前函数内部生生成一个父类对象,super(当前类名,self).父类的属性直接可以调用父类的数据属性与方法属性,在python3中可以直接使用用self()来生成父类对象
- super是沿着子类的mro()这个继承列表进行查询属性的
组合
在现实世界中,类之间可能存在是与不是 或者 有或没有的关系,比如,学生可以有很多课程,那么可以在学习实例化对象小明的增加课程1,课程2等等属性,那么小明可以直接访问课程1,2等课程对象,也就可以访问课程类的属性与方法了!
class People:
def __init__(self, name):
self.name = name
class Dog:
def __init__(self, name):
self.name = name
def sit_down(self):
print(‘dog %s is sitting dowm‘ % self.name)
p1 = People(‘jim‘)
dog1 = Dog(‘将军‘)
dog2 = Dog(‘旺旺‘)
p1.Dog1 = dog1 # 给jim增加第一只狗
p1.Dog2 = dog2 # jim收养了第二只狗
p1.Dog1.sit_down() # dog 将军 is sitting dowm
p1.Dog2.sit_down() # dog 旺旺 is sitting dowm
print(p1.__dict__) # {‘dog1‘: <__main__.Dog object at 0x7f87b7aaba58>, ‘name‘: ‘jim‘, ‘dog2‘: <__main__.Dog object at 0x7f87b7aaba90>}
抽象类
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的
import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def run(self):
pass
@abc.abstractmethod
def speak(self):
pass
def sleep(self):
pass
class Dog(Animal):
def run(self):
print(‘dog is running‘)
# def speak(self):
# print(‘dog is barking‘)
d = Dog()
d.run()
# d.speak()
- 在若子类继承抽象父类,那么子类必须实现父类的抽象方法,否则子类的实例化过程就会报错
多态与多态性
多态:同一类事物的多种形态
多态性:指在不考虑实例类型的情况下使用实例,多态性分为静态多态性和动态多态性
多态性的好处:
- 1.增加了程序的灵活性, 以不变应万变,不论对象千变万化,使用者都是同一种形式去调用
peo=People()
dog=Dog()
pig=Pig()
# peo、dog、pig都是动物,只要是动物肯定有talk方法
# 于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()
# 更进一步,我们可以定义一个统一的接口来使用
def func(obj):
obj.talk()
- 2.增加了程序额可扩展性,通过继承父类创建了一个新的类,使用者无需更改自己的代码,还是用同一个接口去调用
- 鸭子类型: Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子,python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象,也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度
封装
类和对象的隐藏属性
- 采用双下划线加属性名称定义的属性;
- 在类内部可以直接通过obj.__atrrname调用,但是在外部不能通过obj.__atrrname名称调用;
- 子类无法覆盖父类以‘__‘开头的属性, 因为子类的属性和父类的属性名转换后是不一样的,前面加了_类名的前缀;
- 在类定义的阶段隐藏属性可以生效,但是在定义完成后再想隐藏属性是没有用的,即随后通过调用类名或对象定义隐藏属性不会自动转换属性名称
- 隐藏属性的作用
- 封装数据属性:明确区分内外,控制外部对隐藏属性的操作行为
class Teacher:
def __init__(self,name,age):
self.__name=name
self.__age=age
def tell_info(self):
print(‘姓名:%s,年龄:%s‘ %(self.__name,self.__age))
def set_info(self,name,age):
if not isinstance(name,str):
raise TypeError(‘姓名必须是字符串类型‘)
if not isinstance(age,int):
raise TypeError(‘年龄必须是整型‘)
self.__name=name
self.__age=age
t=Teacher(‘egon‘,18)
t.tell_info()
t.set_info(‘egon‘,19)
t.tell_info()
- 封装方法:隔离复杂度
class ATM:
def __card(self):
print(‘插卡‘)
def __auth(self):
print(‘用户认证‘)
def __input(self):
print(‘输入取款金额‘)
def __print_bill(self):
print(‘打印账单‘)
def __take_money(self):
print(‘取款‘)
def withdraw(self):
self.__card()
self.__auth()
self.__input()
self.__print_bill()
self.__take_money()
a=ATM()
a.withdraw()
解释:取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
隔离了复杂度,同时也提升了安全性封装还可以体现出可扩展性高的优点
property的应用
- 对类的定义的内部函数加上@property装饰器函数,那么外部访问此方法属性直接可以通过obj.函数名调用此函数,结果返回要获得的值
class People:
def __init__(self, name, age, sex):
self.__name = name
self.__sex = sex
self.__age = age
@property
def info(self):
print(‘姓名:<{name}> 年龄:<{age}> 性别:<{sex}>‘.format(name=self.__name, age=self.__age, sex=self.__sex))
p1 = People(‘jim‘, 28, ‘male‘)
p1.info
类内部的方法分类
在类内部定义的函数,分为两大类:
- 绑定方法:绑定给谁,就应该由谁去调用,谁来调用就把调用者当做第一个参数自动传入。对象和类都可以调用,仅仅的区别在于传输的参数
- 绑定到对象的方法: 在类内部定义的没有被任何装饰器修饰的函数;
- 绑定到类的方法: 在类内部定义且有装饰器classmethod修饰的方法
- 非绑定方法: 在类的内部定义,被装饰函数staticmethod修饰的函数。它不能自动传值, 不能与类或者对象绑定,任何对象或者类都可以调用,就是一个普通的函数
class Account:
bank=‘CBC‘
def __init__(self, username, password, balance):
self.__Username = username
self.__Password = password
self.__balance = balance
def charge(self, amount):
if not isinstance(amount, int):
print(‘请输入数字金额‘)
return
if int(amount) < 0:
print(‘输入金额有误‘)
return
self.__balance += amount
def check_account(self):
print(‘账户余额: %s‘ % self.__balance)
@classmethod # 绑定到类的方法
def check_bank(cls):
print(‘所属银行:<%s>‘ % cls.bank)
@staticmethod # 静态方法,非绑定方法
def del_bank():
del Account.bank
反射
通过字符串来映射到对象的属性
hasattr(obj, ‘key‘)
,判断对象obj内部有没有key这个属性名;getattr(obj, ‘key‘, None)
,获取对象obj内部的key的属性,相当于obj.key,若没有返回空;setattr(obj, ‘key‘, ‘value‘)
, 可以直接增加obj的key的属性,并将其值设定为valuedelattr(obj, ‘key‘)
, 删除obj对象的属性值
class fileObj:
def read(self):
print(‘read files‘)
def write(self):
print(‘write files‘)
def command(self):
while True:
command = input(‘>>> ‘).strip()
if not command:
print(‘不能为空‘)
return
if hasattr(self,command):
fun =getattr(self, command)
fun()
break
file = fileObj()
file.command()