python面试基础问题
1.arg元组类型和**kwargs字典类型
#元组参数:元组类型数据,对传递参数有顺序要求
def test(*args):
print(f"args[0]={args[0]},args[1]={args[1]},args[2]={args[2]}")
test(1,2,3)
#字典参数:字典类型数据,对传递参数没有顺序要求,格式要求key = value值
def test2(**kwargs):
print(f"name={kwargs['name']},age={kwargs['age']},gender={kwargs['gender']}")
test2(name='Tom',age=20,gender='male')
#注意:无论是包裹位置传递还是包裹关键字传递,都是一个组包的过程。
2.对于一个函数num, 当调用 num(1,2,a=3,b=4)
和调用 num(3,4,5,6,a=1)
以及 num(a=1,b=2)
的时候都可以正常运行,并且可以对元组以及字典类型进行遍历输出,对字典类型进行输出字典的键值对(形式为:key:a,value:1),写函数num?
def num(*args,**kwargs):
for i in args:
print(i)
for k,v in kwargs.items():
print(f"key:{k},value:{v}")
num(1,2,3,4,5,name='Tom',age=20,gender='male')
#结果如下:
# 1
# 2
# 3
# 4
# 5
# key:name,value:Tom
# key:age,value:20
# key:gender,value:male
3.当我们需要使用格式化输出一个 %
号时,需要在后边写两个 %%
print("%d%%及格"%(50))#50%
4.字符串切片问题
[start:stop:step]
start
:起始位置的索引(包含)。如果未指定,则默认为序列的起始位置。
stop
:结束位置的索引(不包含)。如果未指定,则默认为序列的结束位置。
step
:步长,用于指定取元素的间隔,默认为1。
a="abcdefg"
print(a[::])#start:end:step都是默认,从头到尾输出"abcdefg"
print(a[3::-2])#从第3个开始,步长为-2,输出"db"
print(a[6::-2])#从第6个开始,步长为-2,输出"geca"
print(a[1:6:2])#从第1个开始,步长为2,输出"bdf"
print(a[::-1])#步长为-1,反向输出,输出"gfedcba"
print(a[::2])#步长为2,输出"aceg"
print(a[1::2])#步长为2,从第1个开始,输出"bdf"
5.在字典中,如果key是数字类型,如果两个值相等,则后面这个key会替换前面这个key的value值。下面1和1.0值相等,value1.0直接替换value1,也就是3。
总结:
(1)字典的键在比较时考虑的是相等性(__eq__
),而不是身份(is
)。
(2)整数和浮点数在数值相等时,在字典的键比较中会被视为相等,但这并不意味着它们在字典内部存储时会合并为一个键。
(3)字符串和整数(或浮点数,如果它们表示相同的数值)在字典中总是被视为不同的键。
my_dict = {}
my_dict[1] = 1
my_dict['1'] = 2
print(my_dict) #{1: 1, '1': 2}
my_dict[1.0] = 3
print(my_dict) #{1: 3, '1': 2}
print(my_dict[1] + my_dict['1'] + my_dict[1.0]) # 8
6.深拷贝、浅拷贝、赋值三个区别?(这些复制会影响原始数据和副本数据之间的关系)
(1)深拷贝
深拷贝不仅创建了一个新的对象,而且递归地复制原始对象中包含的所有对象(无论嵌套多深),使得新对象和原始对象完全独立。对深拷贝对象所做的任何修改都不会影响到原始对象。
import copy
a = [1, 2, [3, 4]]
b = copy.deepcopy(a) # 深拷贝
b[2].append(5)
print(a) # 输出 [1, 2, [3, 4]],a和b完全独立
(2)浅拷贝
浅拷贝会创建一个新的对象,这个新对象有着原始对象属性值的精确拷贝。但是,如果原始对象的属性值本身也是对象(即嵌套对象),则浅拷贝不会递归地复制这些嵌套对象,而是复制它们的引用。因此,对于嵌套对象,原始对象和浅拷贝对象之间仍然存在共享关系。
import copy
a = [1, 2, [3, 4]]
b = copy.copy(a) # 浅拷贝
b[2].append(5)
print(a) # 输出 [1, 2, [3, 4, 5]],因为a和b共享第三个元素的引用
(3)赋值
赋值是最简单的数据传递方式。当你将一个对象赋值给另一个变量时,你实际上是在创建一个指向原始对象引用的新变量,而不是创建一个新的对象。因此,两个变量都指向内存中的同一个对象。对其中一个变量所做的任何修改都会反映到另一个变量上,因为它们共享同一个数据。
a = [1, 2, 3]
b = a
print("赋值")
print(id(a),a) # 2852076541632 [1, 2, 3]
print(id(b),b) # 2852076541632 [1, 2, 3]
b.append(4)
print(id(a),a) # 2852076541632 [1, 2, 3, 4]
print(id(b),b) # 2852076541632 [1, 2, 3, 4]
7.数学方法趋近于值
通过蒙特卡罗方法估计一个特定的数学表达式。具体来说,它通过生成大量的随机点,并计算这些点满足特定条件的比例,来估计表达式 (math.pi - 2) / (4 - math.pi)
的值。
#import random
def foo(n):
random.seed()
c1=0
c2=0
for i in range(n):
x=random.random()
y=random.random()
r1=x*x+y*y
r2=(1-x)*(1-x)+(1-y)*(1-y)
if r1<=1 and r2<=1:
c1+=1
else:
c2+=1
return c1/c2
print(foo(10000000))#n无限大的时候结果趋近于(math.pi-2)/(4-math.pi)
-
简述python中的内置装饰器@classmethod,@staticmethod和@property作用及区别?
(1)classmethod
@classmethod
装饰器用于将类中的方法转换为类方法,类方法接收类本身作为第一个参数(通常命名为cls
),而不是类的实例。这使得类方法可以在不创建类实例的情况下被调用,并能够访问类的属性(包括类变量和类方法)。建议直接使用,不用实例化对象。class MyClass: count = 0 # 类变量 @classmethod def get_count(cls): """返回类的实例数量""" return cls.count @classmethod def increment_count(cls): """增加类的实例数量(注意:这里只是演示,实际用途可能不同)""" cls.count += 1 # 使用类方法 print(MyClass.get_count()) # 输出: 0 MyClass.increment_count() print(MyClass.get_count()) # 输出: 1
(2)staticmethod
@staticmethod
装饰器用于将类中的方法转换为静态方法,在类的静态方法中无法调用任何类属性和类方法,因为静态方法没有类似 self、cls 这样的特殊参数,因此 Python 解释器不会对它包含的参数做任何类或对象的绑定。class MyClass: @staticmethod def helper_function(a, b): """这是一个静态方法,用于演示静态方法的用法""" return a + b # 使用静态方法 result = MyClass.helper_function(1, 2) print(result) # 输出: 3
(3)property
用于将方法转换为只读属性,以提供更灵活的属性访问方式(包括setter和deleter用于写操作)。
class Person: def __init__(self, name): self._name = name # 使用下划线前缀表示私有属性 @property def name(self): """Getter方法,返回_name的值""" return self._name @name.setter def name(self, value): """Setter方法,设置_name的值""" if not isinstance(value, str): raise ValueError("Name must be a string") self._name = value # 使用Person类 p = Person("Alice") print(p.name) # 输出: Alice p.name = "Bob" # 使用setter方法设置新值 print(p.name) # 输出: Bob # 尝试设置非字符串类型的值,将引发异常 # p.name = 123 # 这将抛出ValueError
-
setter的作用?
setter
并不是一个独立的内置装饰器,而是与@property
装饰器一起使用的一个特性。通过@property
装饰的方法只能被读取(像访问属性一样),但可以通过定义同名但带有.setter
后缀的方法来允许外部代码设置这个属性的值。class Animal: def __init__(self,color): self.color = color @property def color(self): return self.__color @color.setter def color(self,color): self.__color = color animal = Animal("red") print(animal.color)#red animal.color = "blue" print(animal.color)#blue 属性值被改变了
10.remove()
:remove()
方法会根据元素本身的值进行删除。要注意的是:①该方法只会删除遇见的第一个指定元素,如果列表中还有相同的元素是不会被删除的;②必须保证要删除的元素在列表中是存在的,否则会报错。
list = [1, 1, 1, 2, 3, 4, 5]
list.remove(1)
print(list) #[1, 1, 2, 3, 4, 5]
list.extend([6, 7, 8])
print(list) #[1, 1, 2, 3, 4, 5, 6, 7, 8]
11.递归算法
① 简化问题:找到最优子问题(不能再小)
② 函数自己调用自己
#斐波拉契数列1,1,2,3,5,8,13,21,34,55,89,144...
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
print(fib(10))#55
#递归求数的阶乘
def a(n):
if n<2:
return 1
else:
return n*a(n-1)
print(a(5))#120
12.猴子吃桃问题
猴子第1天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。第2天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半另加一个。到第10天早上想再吃时,就只剩下一个桃子了。求第1天共摘了多少个桃子?
第十天 1个
第九天(1+1)*2=4个
第八天(4+1)*2=5个
............以此类推
def f(n):
if n == 10:
return 1
return (f(n+1) + 1) * 2
# 调用函数
print(f"第一天摘了{f(1)}桃子")#1534