掉进悬崖的小白,总结python基础第一阶段,面向对象复习题刷起来!
1.函数相关
函数的含义
广义:
为获得某种东西或达到某种目的而采取的手段与行为方式。
狭义:
函数是指由一系列的程序语句组成的代码块
特性:
1.带名字的代码段
2.函数是代码重复使用的重要手段,是重用的基本手段:
无论现实世界还是程序世界,都以函数来达到重用的目的
函数的定义
定义语法:[]表示可选 <>表示必备
<函数名>([参数列表])<:>
待执行语句
如果有需要显式返回<return 标识符>或
函数的参数
形式参数:由函数编写者在预定义时限定
实际参数:任意的数据类型(为形参赋值)在函数的调用者中定义赋值
函数的调用过程
1.调用者和被调用者相互可见,采用直接调用的方式 函数名()
2.调用者和被调用者相互不可见,先import模块再调用:注意调用时带上模块.
3.特殊的调用方式,(调用类方法(类名.),调用成员方法(对象.))
析构函数等魔法函数需要用特殊的方案调用(在类内使用CTRL+o选择调用,或者自己打def._ _ xxx_ _)
函数的返回值
返回值类型:
所有函数都需要返回
只是没写return的会自动返回None
函数有无返回值取决于函数的调用者是否需要返回值
注意:
函数遇到return立即退出并返回 so也常作为控制方法结束的手段
函数的传参规则
参数传递需要明白的要点:
形参的修正对实参的影响程度
参数传递的方式:
形式参数 = 实际参数
传参规则:
根据形式参数类型的不同 对实际参数的影响程度也不相同
A:不可变类型:字符串、数字、布尔、元组
B:列表、类、对象、集合、字典
函数内部形参修正对函数外实参的影响程度:
A:不可变类型 不影响实际参数
B:影响实际参数(如列表,函数内部不修改整个列表时,不影响实参列表,只是修改了列表的内部)
2.注释的使用
#、单行注释
“”“、”“”、多行注释(_ doc _ 访问注释)
‘’‘ ’‘’多行注释(不推荐)
3.访问控制的种类及其作用范围
完全公开: 标识符不以_开始
保护(单下划线前缀) :_标识符
私有(双下划线前缀) :__标识符
完全公开:任意位置均可访问
保护:当前类及子类可访问
私有:只能在当前类内访问
4.什么是面向对象
面向对象编程(Object Oriented Programming)简称OOP:
用认识一个系统的方式来分析、设计和实现一个系统
面试答案:封装、继承和多态即为面向对象
5.如何抽象出类 怎么抽象
想象这个事物有什么(属性),能做什么(方法)
要求:
数据抽象(变量)—描述某类对象的状态(对象相互区别的物理量);
代码抽象(函数)—描述某类对象的共有的功能。
如何实现抽象:
1.分析事物,提取其状态(变量)和行为(函数);
2.要有主次:关注重要的,忽略不重要的
6.类怎么定义
类(class):一种复杂的自定义数据类型
类之重要:面向对象的核心
类的定义语法:
class <类名>(父类列表):
构造方法 (制造对象模板,开辟对象地址空间)
类变量…(类内共用,可以在未创建类的对象前就用类名直接调用类属性)
初始化方法(self,对象的变量…)(对象实例化)
类方法(1.在未产生对象的情况下即可使用类名访问 2.对类变量进行内部封装处理)
静态方法(和类和对象都无关,只是一个功能,用来表达层级关系)
属性
成员方法/对象方法
析构方法(垃圾回收)
7.类内部都有什么内容 各有什么作用
8.怎么使用类内部定义的内容 请详细点说明
使用类的方式:和一般类型一致(声明->赋值->使用)
如何声明:
对象引用 = 类名(构造中需要的实参)
使用:
通过对象引用来寻找对象的变量和方法
对象引用.对象的变量
对象引用.对象方法([参数列表])
对象的构造过程
1.为对象开辟空间,使用__new__魔术方法;
2.调用初始化方法__init__初始化;
3.返回对象的引用。
类和对象的关系:
1.类是对象的模板
2.一个类可以创建多个对象
3.对象是类一个特定的个体
4.对象有独立的内存空间,不会互相影响
9.self的作用
self :
对象方法中必备的第一个参数
声明:初始化及每个对象方法必须写,且必须为第一参数
作用:
1.初始化或对象方法被调用时,立即自动指向调用该方法的对象,无需显式传参(代表当前对象)
2.区分同名的局部变量和对象变量
10.封装的含义
将字段(数据成员)和行为(代码成员)相组合的一种机制。
目的:
1.控制对象状态的范围
2.加强对象自身的内联(联动)性
3.便于外部使用
11.怎么实现封装
封装的基本要求:
特定边界:所有的内部变化都限制在此边界内(类内部);
特定访问权限:在对象外部不能访问或修改受保护的内部实现细节(_ _成员)
有外部接口(set/get方法):此对象利用它与其它对象发生关联
使用属性装饰器来完成封装:
主要用于快速构建代码:
prop+回车:生成只读属性
props+回车:生成可读写属性
propsd+回车:生成可读写删属性
12.继承的含义
继承是由已有的类创建新类的机制。
由继承得到的类称为子类(派生类)
被继承的类称为父类(超类)(基类)
继承的作用:
实现软件可重用的重要方式
增强软件可扩充性
提高软件的可维护性
13.派生类(子类)继承到了父类的什么内容?
可继承的内容:
子类继承父类私有以外的成员变量
:需要super().init(父级初始化参数)或使用父级统一的初始化方法
子类继承父类私有以外的成员方法
子类继承父类的析构方法
子类不能删除父类的成员
子类可以覆盖/重写父类成员方法
子类可以增加自己独有的成员
子类对象对父类成员的访问权限:
子类可以访问父类私有成员(__XXX)以外的内容
14.怎么实现封装
同11题
封装时注意避免深度递归
函数直接或间接调用函数本身,则该函数称为递归函数
class Person:
def __init__(self,name) -> None:
super().__init__()
self.name = name
@property
def name(self):
return self.name # 注意用下划线区分,避免深度递归
15.super的用法
super() 用法
用super()可以实现对父类成员的访问。
调用父类中被覆盖的方法,如:
super().method([paramlist])
调用父类的初始化函数,如:
super().init([paramlist])
16.多继承类的方法搜索顺序(python3)
python3为广度优先遍历(mro()),先搜索第一位继承类的方法,再搜索该类的其它方法,最后搜索第二位继承类的方法…
17.子类怎么对继承到的内容进行进一步重写
子类可以覆盖/重写父类成员方法
18.子类对象生成时需要生成的所有对象的具体过程
欲要有子,必先有父,父类的对象属性生成完,才能参数子类的对象属性。
19.多态的含义
即一个名字具有多种语义。
在面向对象中指一个方法可以有多种实现版本。
类的多态表现为方法的多态
方法的多态:覆盖(override)。
覆盖(重写)要求条件:
1.父子类中
2.子类中的方法务必和父类的方法同名
3.参数个数应保持一致
4.其他的通通一样
20.多态的好处
增强代码可维护性
提高程序的扩展性
可以用一个参数管理具有鸭子模型的多个对象
21.如何辨识对象是否为本类对象
判断对象是否为某类实例
isinstance(o,cls):辨识o是否为cls对应类的实例或子类实例
print(isinstance(so, SubObject)) # True
print(isinstance(so, object)) # True
type(o)/o.class:按对象对应的具体类型来比对 最精确
print(type(so) is SubObject) # True
print(so.class is SubObject) # True
print(type(so) is object) # False
print(so.class is object) # False
22.如何辨识某类是否是某类的子类
issubclass(sub_cls, super_cls):辨识sub_cls是否为super_cls子孙类
print(issubclass(SubObject, object)) # True
23.如何按内容比对对象
运算符重载
‘_ _ eq__’, ==
‘_ _ ge__’, >=
‘_ _ gt__’, >
‘_ _ le__’, <=
‘_ _ lt__’, <
‘_ _ ne__’, !=
==比较是__eq__()魔法方法的返回值
__eq__()方法未显式在类中,若未重写,继承自object或自身父类
object中的__eq__()仍然比对对象的地址 so此时仍为False
若有需求认为对象内容相同即为相等 需要重写__eq__():
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
pass
def __eq__(self, other):
return self.name == other.name and self.age == other.age
此时:
print(s1 == s2) # True
24.如何在直接输出对象时按格式自动解析输出
一般类中重写将对象解析为字符串
‘str’,
25.如何获取标识符内地址
id()
26.你都知道哪些魔法方法,说说作用
信息获取:
‘_ _ class__’, 获取类型
‘_ _ dir__’, 获取全描述
‘_ _ doc__’, 获取类及类中方法的注释
‘_ _ sizeof__’, 获取对象字节长度 默认返回引用长度32
属性相关:
删除对象对象属性时执行的方法
‘_ _ delattr__’,
获取找不到的属性时执行的方法
‘_ _ getattribute__’,
设置属性时执行的方法
‘_ _ setattr__’,
运算符重载
‘_ _ eq__’, ==
‘_ _ ge__’, >=
‘_ _ gt__’, >
‘_ _ le__’, <=
‘_ _ lt__’, <
‘_ _ ne__’, !=
集合相关:
‘_ _ hash__’,
构造:
‘_ _ new__’,
初始化:
‘_ _ init__’,
对象自动解析:
交互式时使用
‘_ _ repr__’,
一般类中重写将对象解析为字符串
‘_ _ str__’,
格式化对象时
‘_ _ format__’,
归约相关
‘_ _ reduce__’,
‘_ _ reduce_ex__’,
生成子类对象时:
‘_ _ init_subclass__’,
检测子类方法
‘_ _ subclasshook__’
27.如何为类和对象动态追加属性
Python允许动态组装class
class的实例属性和类属性甚至方法成员都可以动态添加
例:
class Student:
pass
# 动态添加类属性
Student.stu_count = 0
print(Student.stu_count) # 0
# 动态添加实例属性
s1 = Student()
s1.name = "张三"
print(s1.name) # 张三
# 动态添加实例方法
def show():
print("show info")
pass
s1.info = show # 添加实例方法
s1.info()
s2 = Student()
s2.info() # error
# 注意:动态添加的实例属性只能当前对象使用 若需要所有对象都能使用,请使用类名.进行动态添加(这种方式动态添加的变量是类变量,动态添加的方法要看方法的形式)
# 修改上面的代码:
def show(self):
print("show info")
pass
Student.info = show # 添加实例方法
s1.info()
s2 = Student()
s2.info() # ok
# 删除动态追加的内容:
对象名追加对象属性
# del obj.a
delattr(obj,"a")
类名追加类属性
del Foo.b
类名追加对象方法
del Foo.o_fun
28.如何限定实例可追加的属性
限定属性:
语法:
class limit_class:
slots = (“可动态添加的实例属性名”, …, …)
作用:
限定对象的属性名列表,防止随意动态添加实例属性
案例:
class User:
__slots__ = ("name", "age", "tel", "address")
def __init__(self, name, age, tel) -> None:
self.name = name
self.age = age
self.tel = tel
pass
user1 = User("admin", 19, "18866668888")
user1.address = "郑州市经开区8888号"
print(user1.name)
# user1.money = 10000 # error 不能追加未出现在限定组内的属性
29.类属性及类方法的意义
类属性:
直接在class中声明的变量
例如:
class Student:
stu_count = 0
pass
stu_count即为类属性
特点:
1.可以在未创建类的对象前就用类名直接调用类属性
2.被所有对象共用
类方法:
语法:
@classmethod
def methodname(cls):
pass
cls和self类似,只是绑定的是class自身
作用:
1.在未产生对象的情况下即可使用类名访问
2.对类变量进行内部封装处理
30.静态函数的意义
函数声明在了类的内部,无法在方法内访问类的其他内容
语法:不能绑定self\cls
@staticmethod
def methodname():
pass
作用:
1.在未产生对象的情况下即可使用类名访问
2.管束类的生成:构造函数__new__
3.函数集
4.多用于设计模式中
31.构造函数和初始化方法的区别
通常来说,新式类进行实例化时,new 方法会返回当前类的实例,然后调用该类的 init 方法进行实例的初始化。但是,如果 new 方法返回的不是当前类的实例,那么,当前类的 init 方法就不会被调用。
个人理解:构造函数打造模板,开辟内存空间,初始化根据模板生成对象实例初始化。
32.单例模式步骤有哪些
from typing import Any
class Book:
def __new__(cls, name) -> Any:
print("类的构造开始执行 ,对象出生")
return super().__new__(cls)
def __init__(self, name) -> None:
super().__init__()
self.name = name
print("初始化方法执行了")
def __str__(self):
return self.name
class BookDb:
_instance = None
_init_flag = False
def __new__(cls) -> Any:
if cls._instance is None: #判定是否开辟过地址空间
cls._instance = super().__new__(cls) #开辟地址空间,把地址给_instance
return cls._instance #已经开辟地址空间,给它_instance的地址
def __init__(self) -> None:
if not BookDb._init_flag:
super().__init__()
self.__books = []
BookDb._init_flag = True # 确定只产生一个对象
def add_book(self, book):
self.__books.append(book)
def show_books(self):
for i in range(len(self.__books)):
print(self.__books[i])
book_db = BookDb()
book_db.add_book(Book("红与黑"))
# 若此时有另外的程序也需要书籍仓库
book_db2 = BookDb()
book_db2.add_book(Book("编程从精通到放弃"))
# book_db.show_books()
book_db2.show_books()
33.类定义时请小心深度递归
封装属性时,注意用下划线区分开来。
@property
def name(self):
return self.name # 注意用下划线区分,避免深度递归
附加 == VS is
对于不可变类型:
==一般情况下比较value
a = 10
b = 10
# so:
a == b # True
is比较标识符内存放的地址
id(var):可以获取标识符内地址
只要id(x)得到的数据一致即为True 反之为False
a = "1"
b = "1"
print("a内地址:", id(a))
# a内地址: 2940295543976
print("b内地址:", id(b))
# b内地址: 2940295543976
print(a is b) # True
可变类型:对象类型
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
pass
s1 = Student("zhangsan", 17)
s2 = Student("zhangsan", 17)
is仍然比对地址及id(x)的值 所以仍然为False
==比较是__eq__()魔法方法的返回值
__eq__()方法未显式在类中,若未重写,继承自object或自身父类
object中的__eq__()仍然比对对象的地址 so此时仍为False
若有需求认为对象内容相同即为相等 需要重写__eq__():