第四章 面向对象

Posted on 2019-09-13 15:31  麻世骞  阅读(154)  评论(0编辑  收藏  举报

1、简述面向对象的三大特性。

  继承:如果多个类中有共同的方法,就可以把共同的方法写在基类也就是父类中,可以供其所有的子类也就是派生类使用,这样的话就不用将这个方法写很多遍了,使得代码更加简洁,提高了代码的编写效率,子类可以继承父类的非私有属性和方法。

  在视图、版本、认证、分页中使用过。

  封装:把多个相关的方法、属性封装在一个类中。

  多态:有多种表现形式。

2、什么是鸭子模型?

  鸭子模型:如果一个东西的叫声像鸭子,走路姿势像鸭子,游泳姿势像鸭子,那么就可以看作这个东西是鸭子。

  在面向对象中,一个类的有效定义不是由它继承的类或者特定的借口决定的,而是由当前方法和属性决定的。

3、super的作用?

  super()函数是用来调用父类的一个方法。在多继承中,为了避免在两个父类由同样的方法时调用出错的问题,就需要super方法了,单继承中,如果子类的方法和父类重名了,也需要super()方法用super也是一个良好的习惯,

  会在以后避免很多出错。

4、mro是什么?

  当两个父类中出现重名方法的时候,如何处理他们的继承关系,即MRO(method resolution order)方法解析顺序问题。

5、什么是c3算法。

  c3算法最早是用于lisp,后来用到python中解决深度优先搜索中不满足本地优先级和单调性的问题。(用来计算类的MRO)

  实例:

class A:
    pass
class B(A):
    pass
class C(A):
    pass
class D(B, C):
    pass
class E(C, A):
    pass
class F(D, E):
    pass
class M:
    pass
class N(M):
    pass
class P(E, A):
    pass
class X:
    pass
class Q(P,N,X):
    pass
class G(Q, F):
    pass
class H(G, F):
    pass

  计算H的mro:

L(A) = A
L(B) = B + L(A) + BA                 BA
L(C) = C + L(A) + A                  CA
L(D) = D + L(B) +L(C) + BC           DBCA
L(E) = E + L(C) +L(A) + CA           ECA
L(F) = F +L(D) + L(E) + DE           F + DBCA + ECA = FDBECA
L(M) = M
L(N) = N + L(M) + M                  NM
L(P) = P + L(E) +L(A) + EA           PECA
L(X) = X
L(Q) = Q + L(P) + L(N) + L(X) + PNX   QPECANMX
L(G) = G + L(Q) + L(F) + QF           G + QPECANMX + FDBECA = GQPFDBECANMX
L(H) = H + L(G) + L(F) + GF           H + GQPFDBECANMX + FDBECA = HGQPFDBECANMX

6、列举面向对象中带双下划线的特殊方法。

  __init__: 构造函数,在生成对象时调用。

  __del__: 析构函数,释放对象时使用。

  __repr__: 打印,转换。

  __new__: 构造类的实例化。

  __setitem: 按照索引赋值。

  __getitem__: 按照索引获取值。

  __len__:获取长度。

  __call__:调用。

7、双下划线和单下划线的区别。

  单下划线:方法或者属性前面,认为它时该方法或者属性为该类的私有属性或者方法。

  双下划线:python设计此的真正目的是为了避免字类覆盖父类的方法。

  开头结尾双下划线:一般来说__this__这种开头结尾都加双下划线的方法表示这是python自己调用的,你不要调用。

8、实例变量和类变量的区别。

  类变量:定义在类里面,通过类名或对象名引用,如果是通过对象名引用,会先找有没有这个同名的实例变量,如果没有,引用到的才是类变量,类变量的更新,只能通过类名,形如 类名.a = 55 ,不要指望通过实例引用类变量来更

  新类变量。

  实例变量: 定义在方法里面的变量,一般在__init__里面,只能通过对象名引用,实例变量的增加、更新形式,形如self.a = 55 。

  区别在于,类变量属于类,一个方法将其改变,其他方法调用的就是改变之后的了,实例变量是属于方法私有的,方法将其改变之后,对其他方法没有影响。

9、静态方法和类方法的区别。

  类方法:定义:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);

  静态方法:定义:使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;调用:实例对象和类对象都可以调用。

10、isinstance 和 type 的作用。

  isinstance: 判断一个对象是某个类或子类的实例。(isinstance(object,type-or-tuple-or-class) -> bool)

  type: 得到传入的object的类型。(type(object)--类型)

11、有用过with statement吗?它的好处是什么。

  用过,打来文件的时候,with的作用是在代码执行结束后,自动关闭文件,不论程序是以何种方式结束的,如果执行代码过程中发生异常,with会在外部异常前关闭文件。

12、下列数据哪些是不可迭代的?

  object不可迭代

13、实现一个singleton单例类,要求遵循基本语言编程规范。

  单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

def Singleton(cls):
    _instance = {}

    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]

    return _singleton


@Singleton
class A(object):
    a = 1

    def __init__(self, x=0):
        self.x = x


a1 = A(2)
a2 = A(3)

14、请描述with的用法,如果自己的类需要支持with 语句,需要怎么书写?

  with 后面的语句被求值后返回对象的'__enter__()'方法被调用,这个方法的返回值将会返回给as后面的变量。

  当with后面的代码全部执行完后将调用前面返回对象的'__exit__()'方法。

  只需要在类里写入__enter__()方法和__exit__()方法即可使用with.

15、如何判断一个对象是可调用对象?

  1、使用内置函数callable函数:可以检测对象是否可以调用,返回True,不一定可以调用,但是返回False,一定不可以调用。

  2、判断对象是否有__call__方法。

  3、判断类型是否是FunctionTypetype(func) is FunctionType 或者 isinstance(func, FunctionType)

  能实现类中__call__()方法的就是可调用类。

16、请实现一个栈

class zhan(object):

    def __init__(self):
        self.items = []

    def panduan(self):
        return self.items == []

    def size(self):
        return len(self.items)

    def peek(self):
        return self.items[len(self.items) - 1]

    def push(self, item):
        self.items.append(item)

    def pop(self):
        return self.items.pop()


obj = zhan()
obj.push('h')
obj.push('a')
obj.push('s')
print(obj.size())
print(obj.peek())
print(obj.pop())
print(obj.peek())
print(obj.size())
print(obj.pop())
print(obj.size())
print(obj.panduan())

17、关于python 类的继承不正确的说法是?

  python的类无法继承,只能有一个父类,可以继承,但是不能继承父类的构造函数。(以上说法是不正确的)

18、------

19、请用两个队列实现一个栈(给出为代码即可)

class zhan1(object):

    def __init__(self):
        self.l1 = []
        self.l2 = []

    def add(self, a):
        if len(self.l1) == 0:
            self.l2.append(a)
        if len(self.l2) == 0:
            self.l1.append(a)

    def pop(self, ):
        if len(self.l1) == 0:
            for i in self.l2[:-1]:
                self.l1.append(i)
            self.l2.clear()
        elif len(self.l2) == 0:
            for i in self.l1[:-1]:
                self.l2.append(i)
            self.l1.clear()
        else:
            print('栈已经为空')

    def check(self):
        if len(self.l1) == 0:
            print(self.l2)
        elif len(self.l2) == 0:
            print(self.l1)


obj = zhan1()
obj.add('a')
obj.add('b')
obj.add('c')
obj.check()
obj.pop()
obj.check()
obj.pop()
obj.check()
obj.add('d')
obj.check()
obj.pop()
obj.check()

  结果:

['a', 'b', 'c']
['a', 'b']
['a']
['a', 'd']
['a']

20、已知如下链表类,请实现单链表逆置。

class Node:
    def __init__(self, value=None, next=None):
        self.value = value
        self.next = next

def nizhuan(link):
    pre = link
    cur = link.next
    pre.next = None
    while cur:
        tem = cur.next
        cur.next = pre
        pre = cur
        cur = tem
    return pre


if __name__ == '__main__':
    link = Node(1, Node(2, Node(3, Node(4, Node(5, Node(6, Node(7, Node(8, Node(9)))))))))
    root = nizhuan(link)
    while root:
        print(root.value)
        root = root.next

21、类的加载顺序

  构造>静态>父类

22、参考下面代码片段

class Context:
    pass


with Context() as ctx:
    ctx.do_something

  结果:

class Context(object):

def __enter__(self):
print('enter')
return self

def __exit__(self, exc_type, exc_val, exc_tb):
print()

def do_something(self):
print('a')


with Context() as ctx:
ctx.do_something()

23、下面代码输出时什么?请给出答案并解释。

class Parent(object):
    x = 1

class Child1(Parent):
    pass

class Child2(Parent):
    pass

print(Parent.x)
print(Child1.x)
print(Child2.x)

Child1.x = 2
print(Parent.x)
print(Child1.x)
print(Child2.x)

Parent.x = 2
print(Parent.x)
print(Child1.x)
print(Child2.x)

  结果:

1
1
1
1
2
1
2
2
2

  解释:父类中的变量类似于全局变量,只能在全局作用域里修改,子类中的属于局部变量,局部变量中对全局变量进行修改只能作用于自己类中,无法改变去他作用域的该变量。

24、

25、请给出下面代码片段的输出,请简述上面代码需要改进的地方。

class singlenton:
    _instance = None
    def __new__(cls, *args, **kwargs):
        print('new')
        if cls._instance is None:
            print('Create')
            cls._instance = super().__new__(cls, *args, **kwargs)
        return  cls._instance

    def __init__(self):
        print('initalize')
        self.prop = None

s1 = singlenton()
s2 = singlenton()

  结果:

new
Create
initalize
new
initalize

  改进:应该把__init__函数放在最上面,

26、请简单解释python中的静态方法和类方法,并将以下代码填写完整。

  类方法:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法)

  静态方法:使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;调用:实例对象和类对象都可以调用。

 class A(object):
    def foo(self,x)
         print ‘executing foo(%s, %s)‘%(self,x)

    @classmethod
    def class_foo(cls,x):
        print ‘executing class_foo(%s, %s)‘%(cls,x)

    @staticmethod
     def static_foo(x):
         print ‘executing static_foo(%s)‘%(x)
 
a= A()
# 调用 foo函数,参数传入 1
a.foo(1)
# 调用 class_foo函数,参数传入 1
A.class_foo(1)
# 调用 static_foo函数,参数传入 1
A.static_foo(1) /  a.ststic_foo(1)

27、

28、编程实现一个先进先出的队列类,能追定初识化时的队列大小。

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())