面试准备(一)面向对象(基础概念相关)

python中的类属性、实例属性、类方法、实例方法

https://blog.csdn.net/loner_fang/article/details/80877491

  • 实例属性和类属性重名

当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问。

class Person(object):
    address = 'zhejiang'
    def __init__(self, name):
        self.name = name
 
p1 = Person('Bob')
p2 = Person('Alice')
 
print 'Person.address = ' + Person.address  # zhejiang
 
p1.address = 'shanxi'
print 'p1.address = ' + p1.address  # shanxi
 
print 'Person.address = ' + Person.address   # zhejiang
print 'p2.address = ' + p2.address  # zhejiang

尾递归调用

  • 一个简单的实现
def fact_iter(num, product):
    if num == 1:
        return product
    return fact_iter(num - 1, num * product)

列表生成式

  • [x * x for x in range(1, 11)]
  • [x * x for x in range(1, 11) if x % 2 == 0]
  • [m + n for m in ‘ABC’ for n in ‘XYZ’]

[‘AX’, ‘AY’, ‘AZ’, ‘BX’, ‘BY’, ‘BZ’, ‘CX’, ‘CY’, ‘CZ’]

生成器的两种方式

定义:generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误

生成器可以用于for循环和next方法

  • 列表生成式改动[] -> ()

g = (x * x for x in range(10))

  • 使用yield

斐波拉契数列
1, 1, 2, 3, 5, 8, 13, 21, 34, …

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

迭代器

定义:可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。把list、dict、str等Iterable变成Iterator可以使用iter()函数:

这是因为Python的Iterator对象表示的是一个数据流,

  • Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。

  • 可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,

  • 所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算

凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

高阶函数

  • map、reduce、filter都是高阶函数,高阶函数的定义为

函数的传入的参数是一个函数名

函数的返回值是一个函数名

  • map函数
    传入一个func,和array,array中的每个元素都会在fun中运行,结果保存返回新的array
  • filter也是遍历所有元素,但只保存运行结果为true的
  • reduce函数会对array序列中的所有项代入func中运行,并将上一次运行的结果作为x,下一项作为y继续运行,一般用于累积,累和
array = [1,2,3,4,5,6]
value = reduce(lambda x,y:x*y,array,10)
print(value) # value = 10*6*5*4*3*2*1
  • sorted用于排序
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']

函数参数

定义默认参数要牢记一点:默认参数必须指向不变对象!

闭包

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

装饰器

  • 不带参数
def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

now = log(now)

  • 带参数的,三层嵌套
def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

now = log(‘execute’)(now)

偏函数

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64

简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单

面向对象-继承

  • 原则1:调用父类函数传入子类实例,父类中的self对象为子类实例
  • 原则2:子类实例化会执行__init__但是并不会执行父类的init,这一点与java不同
  • 原则3:子类对象可以访问到父类的类属性,但无法访问到实例属性
  • 原则4:python2的继承采用深度优先的原则,而python3的调用顺序为广度优先,可以通过mro()或者__mro__【它是通过c3算法生成的列表】,来查看继承的调用的顺序,比如

定义

class A():
    def go(self):
        print ("go A go!")
    def stop(self):
        print ("stop A stop!")
    def pause(self):
        raise Exception("Not Implemented")
class B(A):
    def go(self):
        super(B, self).go()
        print ("go B go!")
class C(A):
    def go(self):
        super(C, self).go()
        print ("go C go!")
    def stop(self):
        super(C, self).stop()
        print ("stop C stop!")
class D(B,C):
    def go(self):
        super(D, self).go()
        print ("go D go!")
    def stop(self):
        super(D, self).stop()
        print ("stop D stop!")
    def pause(self):
        print ("wait D wait!")
class E(B,C):
    pass

执行

a = A()
b = B()
c = C()
d = D()
e = E()
# 说明下列代码的输出结果
a.go()
print('--------')
b.go()
print('--------')
c.go()
print('--------')
d.go()
print('--------')
e.go()
print('--------')
a.stop()
print('--------')
b.stop()
print('--------')
c.stop()
print('--------')
d.stop()
print('--------')
e.stop()
print(D.mro())

打印结果为

go A go!
--------
go A go!
go B go!
--------
go A go!
go C go!
--------
go A go!
go C go!
go B go!
go D go!
--------
go A go!
go C go!
go B go!
--------
stop A stop!
--------
stop A stop!
--------
stop A stop!
stop C stop!
--------
stop A stop!
stop C stop!
stop D stop!
--------
stop A stop!
stop C stop!
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

深度和广度优先的详细介绍https://www.cnblogs.com/zhaof/p/7092400.html

这里详细解释一下:D.mro(),他的打印结果为

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

他的继承顺序为

class D(B,C): # 其中B,C都继承了A

所有根据广度优先的原则,先找到root类,再找出所有的下一级别类,最终顺序为ACBD

那为啥不是ABCD呢?

问得好,我也不知道,我又了下D继承B,C,F

class D(B,C,F): # B,C,F都继承了A

结果打印出来的顺序为:

A-F-C-B-D

结果就很明显了,是按照从右往左排列的

所以结论是:

python3是按照广度继承原则来决定调用顺序的,默认从右到左,不会重复的调用父类

参考博文

https://www.cnblogs.com/xinghuaikang/p/8481712.html
https://www.cnblogs.com/zhaof/p/7092400.html

import的使用

  • 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
  • 相对导入时的注意点:

1、文件夹中必须有__init__.py文件,该文件可以为空,但必须存在该文件。

2、不能作为顶层模块来执行该文件夹中的py文件(即不能作为主函数的入口)。就是说我的函数入口文件不能写在这个包里面,因为这是这个包就变成了顶层模块

参考:

https://www.cnblogs.com/ArsenalfanInECNU/p/5346751.html

python类中的内置属性

1.__slots__属性

用于限制实例的属性

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

使用后student的实例不会再允许使用student.xxx来添加不在tuple中的属性。

注意点:只对这个类的实例生效,不会对其子类生效

除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。

2.@property 修饰符,将方法变成属性调用
class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value
3. __str__类似java中的tostring,重写__repr__达到不敲print直接调用对象输出和__str__一样的效果
4.关于迭代
4.1 重写__iter__和__next__将该类实例变成可迭代的对象,可以用于for循环和next方法
class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a,b

    def __iter__(self):
        return self # 实例本身就是迭代对象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration()
        return self.a # 返回下一个值
4.2 重写__getitem__是对象可以像列表一样使用下标取出元素
class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a

注意:如果要使用切片,getitem中需要另做判断,这里不多赘述,具体可以参考廖雪峰老师的博客

也可以看我以前的博客 https://blog.csdn.net/weixin_42142216/article/details/89363235

4.3 isinstance /issubclass
  • isinstance(obj,cls) 检查是否 obj 是否是类 cls 的对象
  • issubclass(sub, super) 检查 sub 类是否是 super 类的派生类

描述符的使用

在一般情况下,

一 描述符本身应该定义成新式类, 被代理的类也应该是新式类

二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中

三 要严格遵循该优先级, 优先级由高到底分别是

  1. 类属性
  2. 数据描述符
  3. 实例属性
  4. 非数据描述符
  5. 找不到的属性触发__getattr__()

但是,当我们使用实例来调用时,如果这时候同时存在与实例属性同名的类属性或函数,实例属性的优先级大于一切!!!

cell方法

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
让对象可以让函数一样加(),就是调用的call方法

new 和init


1、继承自object的新式类才有__new__

2、__new__至少要有一个参数cls,代表当前类,此参数在实例化时由Python解释器自动识别

3、__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类(通过super(当前类名, cls))__new__出来的实例,或者直接是object的__new__出来的实例

4、__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

5、如果__new__传入的cls是自己的类,会自动调用__init__函数,通过return语句里面调用的__new__函数的第一个参数是cls来保证是当前类实例,如果是其他类的类名,;那么实际创建返回的就是其他类的实例,其实就不会调用当前类的__init__函数,也不会调用其他类的__init__函数。


1、在定义子类时没有重新定义__new__()时,Python默认是调用该类的直接父类的__new__()方法来构造该类的实例,如果该类的父类也没有重写__new__(),那么将一直按此规矩追溯至object的__new__()方法,因为object是所有新式类的基类。

2、而如果子类中重写了__new__()方法,那么你可以自由选择任意一个的其他的新式类(必定要是新式类,只有新式类必定都有__new__(),因为所有新式类都是object的后代,而经典类则没有__new__()方法)的__new__()方法来制造实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死循环。反正肯定不能调用自己的__new__,这肯定是死循环。

3、对于子类的__init__,其调用规则跟__new__是一致的,当然如果子类和父类的__init__函数都想调用,可以在子类的__init__函数中加入对父类__init__函数的调用。

4、我们在使用时,尽量使用__init__函数,不要去自定义__new__函数,因为这两者在继承派生时的特性还是很不一样的。

总结

1.new是在init之前调用的,init中的self就是new创建处理的实例

2.new必要有一个参数cls(表示当前类),和一个返回值

3.new的返回值可以return父类(通过super(当前类名, cls))__new__出来的实例,或者直接是object的__new__出来的实例

4.在new中不可调用自己的new,这会造成死循环

5.如果子类中没有重写new,python默认调用父类的new

6.只有继承object的新式类,才有new方法

7.new的简单实用是实现单例设计模式

__del方法:析构方法

垃圾回收时触发

__enter , __exit使类支持上下文管理协议

with Foo(‘a.txt’)as f:
先执行Foo的enter,执行完毕后执行exit

  • with代码块中的异常会触发Exit方法
  • 若exit方法返回True,则该异常会被捕获

实用枚举类定义常量

from enum import Enum, unique

@unique
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

可以实用Type动态创建类

>>> def fn(self, name='world'): # 先定义函数
...     print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>

要创建一个class对象,type()函数依次传入3个参数:

  • class的名称;
  • 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  • class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

metaclass用于控制类的行为

https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值