第四章 面向对象
1. 简述面向对象的三大特性。
继承,封装和多态
- 继承:
- 继承就是继承的类直接拥有被继承类的属性而不需要在自己的类体中重新再写一遍,其中被继承的类叫做父类、基类,继承的类叫做派生类、子类。
- 封装:
- 封装就是把类中的属性和方法定义为私有的,方法就是在属性名或方法名前加双下划线,而一旦这样定义了属性或方法名后,python会自动将其转换为_类名__属性名(方法名)的格式,在类的内部调用还是用双下划线加属性名或方法名,在类的外部调用就要用_类名__属性名(方法名)。父类的私有属性和方法,子类无法对其进行修改。
- 多态:
- 多态就是不同的对象可以调用相同的方法然后得到不同的结果,有点类似接口类的感觉,在python中处处体现着多态,比如不管你是列表还是字符串还是数字都可以使用+和*。
2. 什么是鸭子模型?
鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。如果具有__iter__,__next__就是迭代器
3. super 的作用?
当子类中的方法与父类中的方法重名时,子类中的方法会覆盖父类中的方法,那么,如果我们想实现同时调用父类和子类中的同名方法,就需要使用到super()这个函数,用法为super().函数名()
4. mro 是什么?
类名.mro()可以直接删除当前类的继承执行顺序
- 对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能来自于基类,所以在方法调用时就需要对当前类和基类进行搜索以确定方法所在的位置。而搜索的顺序就是所谓的「方法解析顺序」(Method Resolution Order,或MRO)。
5. 什么是 c3 算法?
c3算法是python新式类中用来产生mro顺序的一套算法。即多继承的查找规则。
6. 列举面向对象中带双下划线的特殊方法。
__new__:可以调用其它类的构造方法或者直接返回别的对象来作为本类的实例。
__init__: 负责类的实例化
__call__:对象后边加括号,触发执行
__str__:print打印一个对象时。
__repr__:print打印一个对象时(没有__str__时执行)。
__enter__与__exit__:配合with使用。
__doc__:类的注释,该属性是无法继承的。
__getattr__:在使用调用属性(方式、属性)不存在的时候触发
__setattr__:添加/修改属性会触发它的执行
__dellattr__:删除属性的时候会触发
__delete__:采用del删除属性时,触发
7. 双下划线和单下划线的区别?
- "单下划线" 开始的成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量;
- "双下划线" 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
8. 实例变量和类变量的区别?
- 实例变量是对于每个实例都独有的数据
- 类变量是该类所有实例共享的属性和方法
9. 静态方法和类方法区别?
- 实例方法:第一个参数必须是实例对象,通常为self。实例方法只能由实例对象调用。
- 类方法:使用装饰器@classmethod。第一个参数为当前类的对象,通常为cls。实例对象和类对象都可以调用类方法。
- 静态方法:使用装饰器@staticmethod。没有self和cls参数。方法体中不能使用类或者实例的任何属性和方法。实例对象和类对象都可以调用。
10. isinstance 和 type 的作用?
- 两者都用来判断对象的类型
- 对于一个对象的直属类型判断可以使用type,但是非直属类型判断就需要isinstance
11. 有用过 with statement(语句)吗?它的好处是什么?
参考链接简单使用
- 使用with后不管with中的代码出现什么错误,都会进行对当前对象进行清理工作。例如file的file.close()方法,无论with中出现任何错误,都会执行file.close()方法
- 只有支持上下文管理器的对象才能使用with,即在对象内实现了两个方法:enter()和__exit__()
12. 下列数据结构中,哪一种是不可迭代的
A. dict B. object C. set D. str
13. 实现一个 Singleton 单例类, 要求遵循基本语言编程规范(用尽量多的方式)。
# 1.__new__实现单例模式 ''' class A(object): __flag=None def __new__(cls, *args, **kwargs): if cls.__flag==None: cls.__flag=object.__new__(cls) # cls.__flag=super(A,self).__new__(cls) return cls.__flag def __init__(self,name,age): self.name=name self.age=age a=A("zhang",12) print(a.__dict__,id(a)) b=A("yang",13) print(b.__dict__,id(b)) print(a.__dict__,id(a)) a.age=22 print(b.__dict__,id(b)) print(a.__dict__,id(a)) ''' #2。装饰器实现单例模式 ''' def wrapper(cls): l=[] def inner(*args,**kwargs): if not l: l.append(cls(*args,**kwargs)) return l[0] return inner @wrapper class A(object): def __init__(self): pass a1=A() a2=A() print("类装饰器单例:",id(a1),id(a2)) ''' #3.外部模块类实例导入 ''' class A(object): def __init__(self): pass a=A() from . import a print(a) ''' # 线程单例 from threading import Thread,Lock def wrap(func): func.__lock__=Lock() def inner(*args,**kwargs): with func.__lock__: return func(*args,**kwargs) return inner class B(object): __flag = None @wrap def __new__(cls, *args, **kwargs): if cls.__flag==None: cls.__flag=super().__new__(cls) return cls.__flag def __init__(self): pass b1=B() b2=B() print(id(b1),id(b2)) #线程基于__new__的单例 import threading class Singleton(object): _instance_lock = threading.Lock() def __new__(cls, *args, **kwargs): if not hasattr(cls, "_instance"): with cls._instance_lock: if not hasattr(cls, "_instance"): cls._instance = object.__new__(cls) return cls._instance def __init__(self): pass #使用元类创建 import threading class SingletonType(type): _instance_lock = threading.Lock() def __call__(cls, *args, **kwargs): if not hasattr(cls, "_instance"): with SingletonType._instance_lock: if not hasattr(cls, "_instance"): cls._instance = super().__call__(*args, **kwargs) return cls._instance class Singleton(metaclass=SingletonType): def __init__(self): pass obj1 = Singleton() obj2 = Singleton() print(obj1 is obj2)
14. 请描述 with 的用法, 如果自己的类需要支持 with 语句, 应该如何书写?
在类中实现两个方法:enter()和__exit__()
class W(object): def __init__(self): pass def __enter__(self): print('进入with语句') return self def __exit__(self,*args,**kwargs): print('退出with语句') with W() as w: print('之前') print(w) print('之后')
15. python 中如何判断一个对象是否可调用? 那些对象可以是可调用对象?如何定义一个类, 使其对象本身就是可调用对象?
- 使用callable函数判断。
- 可调用对象有7类:
- 用户自定义函数
- 内置函数
- 内置方法
- 方法(定义在类中的函数)
- 类
- 类实例(如果类中定义了__call__方法,那么这个类的实例就是可调用对象)
- 生成器函数
- 在类中定义__call__方法,实例对象加()是即调用__call__的方法
16. 请实现一个栈。
""" 栈的抽象数据类型定义:栈的抽象数据类型应该由以下结构和操作定义。栈操作如下: Stack() 创建一个空的新栈。 它不需要参数,并返回一个空栈。 push(item)将一个新项添加到栈的顶部。它需要 item 做参数并不返回任何内容。 pop() 从栈中删除顶部项。它不需要参数并返回 item 。栈被修改。 peek() 从栈返回顶部项,但不会删除它。不需要参数。 不修改栈。 isEmpty() 测试栈是否为空。不需要参数,并返回布尔值。 size() 返回栈中的 item 数量。不需要参数,并返回一个整数。 """ class Stack(object): def __init__(self): self.stack = [] def __str__(self): return str(self.stack) def push(self, item): self.stack.append(item) def pop(self): return self.stack.pop() if self.stack else "Stack is empty!" def peek(self): return len(self.stack) - 1 def isEmpty(self): return self.stack == [] def size(self): return len(self.stack) s=Stack() s.push(1) s.push(3) print(s)
17. 关于 Python 类的继承不正确的说法是?(多选)
A. Python类无法继承 B. 可以继承, 无法执行父类的构造函数
C. 可以有多个父类 D. 只能有一个父类
18. 实现一个 hashtable 类, 对外暴露的有 add 和 get 方法, 满足以下测试代码
1 def test(): 2 import uuid 3 name = {"name", "web", "python"} 4 ht = HashTable() 5 for key in names: 6 value = uuid.uuid4() 7 ht.add(key,value) 8 print("add元素",key,value) 9 10 for key in names: 11 v = ht.get(key) 12 print("get 元素",key, v)
class HashTable(object): def __init__(self): self.dic = {} def add(self,key,value): self.dic[key] = value def get(self,key): return self.dic.get(key)
19. 请用两个队列来实现一个栈(给出伪代码即可)
class Stack(object): def __init__(self): self.head = [] self.tail = [] def push(self, x): self.head.append(x) def pop(self): if self.head == []: return None while len(self.head) > 1: self.tail.append(self.head.pop(0)) self.head, self.tail = self.tail, self.head return self.tail.pop() def __str__(self): return str(self.head) s = Stack() s.push(2) s.push(333) s.push(3) s.pop() s.pop() s.push(1) print(s)
20. 已知如下链表类, 请实现单链表逆置
1 class Node: 2 def __init__(self, value, next): 3 self.value = value 4 self.next = next
class LinkReverse(object): def reverse(self, head): if not head or not head.next:return head#如果链表为空或者只有一个元素是返回本身 #反转关联表的都指针定义 last = None #将原链表的节点指向进行翻转 while head: temp = head.next head.next = last last = head head = temp return last
21. 类的加载顺序(类中有继承有构造有静态)?
- 在堆内存中生成class对象, 把静态变量和静态方法加载到方法区, 这个堆内存中的class对象是方法区数据的入口
- 静态变量默认初始化
- 静态变量显式初始化
- 执行静态代码块
- 成员变量默认初始化, 显示初始化
- 执行构造函数
22. 参考下面代码片段
class Context: pass with Content() as ctx: ctx.do_something()
请在 Context类下添加代码完成该类的实现
class Context: def do_something(self): # print(1122) pass def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): pass with Context() as ctx: ctx.do_something()
23. 以下代码输出是什么? 请给出答案并解释。
1 class Parent(object): 2 x = 1 3 4 class Child1(Parent): 5 pass 6 7 class Child2(Parent): 8 pass 9 10 print Parent.x, Child1.x, Child2.x 11 12 Child1.x = 2 13 print Parent.x, Child1.x, Child2.x 14 15 Parent.x = 3 16 print Parent.x, Child1.x, Child2.x
1 1 1
1 2 1
3 2 3
24. 函数 del_node(self,data)的功能: 在根节点指针为 root 的二叉树(又称二叉排序树)上排除数值为 K 的节点,若删除成功,返回 0,否则返回-1, 概述节点的 定义类型为
1 class Node(object): 2 def __init__(self,data): 3 self.data = data # 节点的数值 4 self.left_child = Node # 指向左右子树的指针 5 self.right_child = Node 6 7 def set_data(self,data): 8 self.data = data
25. 请给出下面代码片段的输出,请简述上面代码需要改进的地方?
1 class Singleton: 2 _instance = None 3 4 def __new__(cls, *args, **kwargs): 5 print("New") 6 if cls._instance is None: 7 print("Create") 8 cls._instance = super().__new__(cls, *args, **kwargs) 9 return cls._instance 10 11 def __init__(self): 12 print("Initalize") 13 self.prop = None 14 15 16 s1 = Singleton() 17 s2 = Singleton()
New
Create
Initalize
New
Initalize
26.请简单解释Python中的staticmethod(静态方法)和classmethod(类方法),并将以下代码填写完整。
1 class A(object): 2 def foo(self,x) 3 print 'executing foo(%s, %s)'%(self,x) 4 5 @classmethod 6 def class_foo(cls,x): 7 print 'executing class_foo(%s, %s)'%(cls,x) 8 9 @staticmethod 10 def static_foo(x): 11 print 'executing static_foo(%s)'%(x) 12 13 a= A() 14 # 调用 foo函数,参数传入 1 15 a.foo(1) 16 # 调用 class_foo函数,参数传入 1 17 A.class_foo(1) 18 # 调用 static_foo函数,参数传入 1 19 A.static_foo(1) / a.ststic_foo(1)
27. 已知一个订单对象(tradeOrder)有如下字段:
字段英文名 |
中文名 |
字段类型 |
取值 |
Id |
主键 |
Long |
123456789 |
Name |
姓名 |
String |
张三 |
Items |
商品列表集合 |
List<商品>(关联商品) |
查找商品对象,一个订单有两个商品。商品字段任意取值 |
IsMember |
是否是会员 |
Boolean |
True |
CouponAmount |
优惠券金额 |
Bigdecimal |
Null |
商品对象
字段英文名称 |
中文名 | 字段类型 | 取值 |
Id | 主键 | Long | 987654321 |
Name | 商品名称 | String | 手机 |
问题:若将订单对象转成 JSON 格式,请书写出转换后的 JSON 字符串。
28. 写代码(栈与队列)
编程实现一个先进先出的队列类, 能指定初始化时的队列大小, 以及enqueue,dequeue,isempty, isfull 四种方法
使用方法如下:
1 s = Queue(2) # 初始化一个大小为 2的队列 2 s.is_empty() # 初始化后, 队列为空, 返回 True 3 s.enqueue(1) # 将 1加入队列 4 s.enqueue(2) # 将 2加入队列 5 s.isfull() # 加入了两个元素, 队列已满, 返回 True 6 s.dequeue() # 移除一个元素, 返回 1 7 s.dequeue() # 移除一个元素, 返回 2 8 s.is_empty() # 队列已经为空, 返回 True
class Queue(object): def __init__(self, size): self.__size = size self.__queue = [] def enqueue(self, x): if len(self.__queue) < self.__size: self.__queue.insert(0,x) else: return 'The queue is full!!!' def dequeue(self): return None if not len(self.__queue) else self.__queue.pop() def is_empty(self): return not len(self.__queue) def isfull(self): return len(self.__queue) == self.__size s = Queue(2) print(s.is_empty()) s.enqueue(1) s.enqueue(2) print(s.isfull()) print(s.dequeue()) print(s.dequeue()) print(s.is_empty())