美文网首页
Python常见的基础面试题(day02)

Python常见的基础面试题(day02)

作者: 莫辜负自己的一世韶光 | 来源:发表于2019-03-11 20:59 被阅读0次

1.Python的自省机制,以及比较常见的自省机制(函数用法)

什么是自省
自省就是程序在运行的时候可以明确的知道对象的类型.

Python中常见的运用到自省机制的方法有哪些?

  • dir()函数
    dir()返回的是传递给它的任何对象的属性名称的列表.如果不指定对象,则dir()返回当前作用域中的名称.
    例如:
>>> import keyword
>>> dir(keyword)
['__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'iskeyword', 'kwlist', 'main']
  • type()
    type()用来确定我们的对象的类型.
>>> type(42)
<class 'int'>
>>> type([])
<class 'list'>
  • hasattr()和getattr()
    用来判断一个对象是否拥有某个属性
>>> hasattr(id, '__doc__')
True
  • isinstance()
    isinstance用来检测某个对象或者类是否属于某个类型.
>>> isinstance("python", str)
True

2.类变量和实例变量

类变量:
1. 类变量是属于类的变量,不依赖于具体的实例对象,在内存中只有一份,所有的实例都共享这个类变量
2. 实例对象和类都可以调用类变量,类变量一般定义在一个类的开头

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/6 14:07'

# 类属性
class Person(object):
    name = 'Tom' # 公有的类属性
    __age = 28   # 私有的类属性

p = Person()
print(p.name) # 实例对象调用类属性
print(Person.name) # 类对象调用类属性
# print(p.__age) # 错误 不能通过类实例直接访问私有的类属性
# print(Person.__age) #错误 不能通过类对象直接访问私有的类属性

实例变量:
1.实例变量是属于具体的实例对象的变量,它依赖对象的存在而存在,每个对象的实例变量都是分开存储的
2.实例变量只有实例对象才可以调用,实例变量一般在__init__()方法中进行定义初始化,或者使用实例加.的方式

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/6 14:12'
class Person(object):
    name = 'Tom'  # 实例属性

p = Person()
p.age = 18

print(p.name,p.age)
# print(Person.age) # 错误,实例属性只能由实例调用,类对象不能调用

print(p.name)
print(Person.name)

p.name = 'Jack'
print(p.name,Person.name)  # 这里打印 Jack Tom 而不是 Jack Jack
# 不能通过实例对象,修改类属性,它只会去创建一个实例属性,并不会改变类属性的值.

3.类方法@classmethod,静态方法@staticmethod,实例方法objectmethod

实例方法:
定义:上面没有装饰器符号,第一个参数必须是实例对象,一般我们用self表示.通过它可以传递实例的属性和方法,也可以传递类的属性和方法.
调用:只能通过实例来调用,类是没有办法调用实例方法的.

类方法
定义:上面有装饰器符号@classmethod,第一个参数必须是类对象,一般用cls来表示.通过它可以传递类的属性和方法,但是不能传递实例对象的属性和方法

调用:实例对象和类对象都可以调用类方法.但是通过实例调用的时候,会被当成是类对象来处理.

静态方法
定义:和普通的函数没有什么区别?对参数没有要求.唯一的区别就是在方法上加一个@staticmethod.但是静态方法不能使用任何类的或者实例对象的属性,它就是一个普通的函数,放到了类中.
调用:类对象和实例对象都可以调用

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/6 14:21'
class Foo(object):
    # 实例方法
    def instance_method(self):
        print("这是类{}的实例方法,只能被实例对象调用".format(Foo))

    @classmethod
    def class_method(cls):
        print('这是类方法')

    @staticmethod
    def static_method():
        print('这是静态方法')

foo = Foo()
foo.instance_method()
foo.class_method()
foo.static_method()

print('下面的方法只能通过实例来调用')
foo.instance_method()
# Foo.instance_method() 错误:类对象不能调用实例方法

Foo.class_method()
Foo.static_method()

3.装饰器@property 和 @xxx.setter的用法

@property
1.这个方法的功能就是将一个属性,当成一个函数来调用.函数名必须和属性名一致.
2.当给一个方法加上@property时,相当于是创建了一个getter方法,可以通过函数的方式获取这个属性的值
3.@property装饰器你本身又创建了另外一个装饰器@xxx.setter

@xxx.setter方法,相当于是给一个属性,加上了setter方法,可以再设置属性加一些判断,这样可以让一个属性的赋值安全和可控,不会再出现随便赋值的情况.

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/11 11:24'


class Student(object):
    def __init__(self, name):
        self.name = name

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

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('值必须是一个整数')
        if value < 0 or value > 100:
            raise ValueError('值必须在0到100之间')

        self.__score = value


if __name__ == '__main__':
    s = Student('fioman')
    s.score = 100  # 直接赋值,相当于是调用了@score.setter装饰器装饰的方法
    print(s.score)  # 获取值,相当于是调用@property装饰器装饰的方法
    s.score = 1000  # 这里会报错
    print(s.score)

通过只定义@property的方法,让一个属性只读,不可以设置更改
如果一个类,它给一个函数只定义了@property装饰器,并没有定义@xxx.setter装饰器,则这个由函数改装之后的属性就是只读的属性,不能够设置更改

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/11 11:35'


class Person(object):
    def __init__(self, name):
        self._birth = None

    @property
    def birth(self):
        return self._birth

    @birth.setter
    def birth(self, value):
        if 0 < value < 2020 and isinstance(value, int):
            self._birth = value
        else:
            raise ValueError('值错误')

    @property  # 年龄自动计算,不可以设置.是只读的属性
    def age(self):
        return 2019 - self.birth


if __name__ == '__main__':
    p = Person('fioman')
    p.birth = 1987
    print(p.age)

这里的birth就是只读的属性,因为它是可以通过生日计算出来的,所以没必要设置.

4.Reverse number(将一个带负号的数反转)

给定一个32位有符号整数,反转这个数显示.
例子:
Input:123
Output:321
Input:-123
Output:-321
Input:120
Output:21

思路,用切片.首先后去这个数的绝对值,然后操作绝对值.如果是负数,就在反转之后的数前面加个负号即可.字符串转换为int的时候,前面如果是0,会自动去除掉.

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/11 17:04'


def reverse_number(num):
    ret = str(abs(num))[::-1] # 先将它的绝对值反转
    return int(ret) if num >=0 else -int(ret) # 根据它是否大于0来决定是否加负号


if __name__ == '__main__':
    print("请输入一个数,可以带符号")
    num = int(input())
    num = reverse_number(num)
    print(num)

5.简述下函数式编程,以及map和zip的使用.

函数式编程是什么

函数也是一个对象,可以看成是一个变量,可以作为参数传入另外一个函数,也可以作为另外一个函数的返回值.闭包和装饰器就是利用了函数式变成.

好处:

一个函数完成简单的功能,多个函数任意组合之后,可以完成更大的功能.

高阶函数

当满足如下条件之一的都是高阶函数.以函数作为参数,或者返回值也是一个函数.

zip函数(打包成一个由元组组成的zip对象)
原型:
zip_object_iterable = zip(*iterable)
将N个可迭代对象打包成一个包含元组的zip对象,并且这个对象也是可迭代的.通过list()可以将其转换为元组列表.元组的个数由可迭代对象中的最小元素个数决定.
也就是说zip是一个打包函数,将后面的可迭代对象分别取出一个元素组成一个元组,然后由这些元组组成一个可迭代的zip对象.

*zip(iterables)是zip的逆过程,将数据还原成打包之前的数据

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/11 19:52'

a = [1,2,3]
b = [4,5,6]
c = [4,5,6,7,8]

zipped = zip(a,b) #返回一个zip对象
print(zipped) #<zip object at 0x0000025348B743C8>
# 转化为列表
list_zip = list(zipped)# 因为zip对象是可迭代的
print(list_zip) # [(1, 4), (2, 5), (3, 6)]

# 同时也可以直接遍历zip对象
for i in list_zip:
    print(i) # (1, 4)(2, 5)(3, 6)

# 当两个可迭代的对象长度不一致时,按最短的那个来生成结果
zipped = zip(a,b,c)
print(zipped)
# 转换为元组
tuple_zip = tuple(zipped)
for i in tuple_zip:
    print(i)
# 1, 4, 4) (2, 5, 5) (3, 6, 6)

print(*zip(a,b,c))
# (1, 4, 4) (2, 5, 5) (3, 6, 6)

map函数(映射)
原型:
iterable = map(function,iterable1,iterable2,...)
作用:

将后面的可迭代对象,作为参数依次带入前面的function,用返回值生成一个新的可迭代对象.
注意参数的个数必须和后面的可迭代的对象的个数一致.还有就是当有一个可迭代对象迭代完成,就结束求值.

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/11 20:20'


# 使用map实现1到9的平方和
def square_number(x):  # 求一个数的平方
    return x * x


if __name__ == '__main__':
    # 使用遍历:
    ret = 0
    for i in range(1, 10):
        ret += square_number(i)
    print(ret)
    # 使用sum函数
    ret = sum(map(square_number, range(1, 10)))
    print(ret)
    # 使用lambda表达式一行实现
    print(sum(map(lambda x : x * x,range(1,10))))

总结:map就是通过第一个参数传递函数,后面的可迭代对象分别作为参数.然后带入前面的函数映射成一个新的可迭代对象

filter函数(过滤)
原型
iterable = filter(function,iterable)
作用:

将一个可迭代对象带入前面的function,如果function的返回值是真,就保留,如果是假,就丢弃过滤掉.
然后将保留的放入到一个可迭代对象中返回.

写一个过滤器,求100到200之间所有素数的和

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/11 20:32'


def isPrimer(num):
    for i in range(2, num):
        if num % i == 0:
            return False
    return True


# 通过filter过滤掉不是素数的数,只保留素数.
ret = sum(filter(isPrimer, range(100, 201)))

print(ret)

写一个过滤器,过滤掉字符串中出现的数字

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/11 20:39'


def filter_number(c):
    if c >= '0' and c <= '9':
        return False
    return True


s = "abcdefg12adf34hg012"

ret = list(filter(filter_number, s))  # 过滤掉,直接转换为列表
print(ret)

# 使用lambda表达式搞定
ret = list(filter(lambda c: False if '0' <= c <= '9' else True, s))
print(ret)

sorted函数(通过比较函数,进行排序的高阶函数)
原型
list = sorted(iterable,key=None,reverse=False)
功能:

传入一个可迭代对象,key是一个只传递一个参数的函数,用它的结果作为比较的键.reverse来决定是否反转.默认是升序.

>>>sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]                      # 默认为升序
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/11 20:50'
# 将一个列表按照它的负数的大小从小到大进行排序
L = [1, 4, 7, 6, 8, 10, 2, 3]
L1 = sorted(L, key=lambda x: x * -1)
print(L, L1)
# 1, 4, 7, 6, 8, 10, 2, 3] [10, 8, 7, 6, 4, 3, 2, 1]
L = [('a', 1), ('b', 2), ('c', 6), ('d', 4), ('e', 3)]
# 按照字符串记性排序
L2 = sorted(L,key=lambda x:x[0])
print(L2)#[('a', 1), ('b', 2), ('c', 6), ('d', 4), ('e', 3)]
# 按照数值降序排序
L3 = sorted(L,key=lambda x:x[1],reverse=True)
# key指定一个只包含一个参数的函数,来制定排序规则
print(L3)

sorted和sort的区别?
sorted不改变原来的可迭代对象,排序之后,原对象位置不变.
sort是在原对象上进行操作,如果位置变动,会改变原来的可迭代对象.

sort是应用在list的方法,而sorted可以应用于所有的可迭代对象.
内建函数sorted方法返回的是一个新的list,而不是在原来的可迭代对象上进行操作.

reduce函数() 对参数序列中元素进行累积
定义:

reduce(function,sequence,[,intial]) -> value
function参数是一个有两个参数的函数,reduce依次从squence中取一个元素和上一次调用function的结果做参数再次调用function.
第一次调用function时,如果提供了initial参数,会以sequence中得一个元素和initial作为参数调用function,否则会以序列sequence中的前两个元素做参数调用function

举例:比如有列表lst = [1,2,3,4,5],我们希望得到每个元素的和,可以这么做.

from functools import reduce

lst = [1, 2, 3, 4, 5, 6, 7]
print(reduce(lambda x, y: x + y, lst))

如果我们希望得到乘积呢

print(reduce(lambda x,y:x*y,lst))

如果有初始化的值,这个时候就不是取列表的前两项,而是取初始值为第一个参数,序列的第一个元素为第二个参数,开始进行lambda函数的应用计算.

print(reduce(lambda x,y : x + y ,lst,100))

相关文章

网友评论

      本文标题:Python常见的基础面试题(day02)

      本文链接:https://www.haomeiwen.com/subject/hhfvpqtx.html