怎么将一个二维字典的第一个key,作为参数传入函数中?

这样的二维字典,是用户-电影-评分字典

img


我的函数是这样的,要传入某个用户,对他推荐电影

img


如何调用?

img


报错

img

通过这个方式获取二维字典第一个key:next(iter(your_dict))

userCF.recommend(next(iter(trainSet)))

[k for k in dic] ,用推导式直接拿出所有键,或者用 dic.keys() 也可以


a = {1:1,2:3,4:5}
print(list(a.keys()))

怎么将一个二维字典的第一个key,作为参数传入函数中?

假如你的字典是example_dict:
那么你的第一个key: example_dict.keys()[0]
第二个key: example_dict.keys()[1]

字典的第一个第二维的key:
example_dict.values()[0].keys()[0]

  • 这个问题的回答你可以参考下: https://ask.csdn.net/questions/7680474
  • 这篇博客你也可以参考下:定义一个函数,传入一个字典和一个元组,key不变将字典的值与元组的值进行交换,返回交换后的字典和元组
  • 除此之外, 这篇博客: 【无标题】中的 列表解析、列表推导式,并将字符串类型的key值全部大写 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • sorted_list = [item.upper() for item in list(d.keys())]
    5. python新式类和经典类的区别
    a.在python里凡是继承了object的类,都是新式类;
    b…
    6. python中内置的数据结构有哪些
    整型int、长整型long、浮点型float、复数complex、字符串str、
    列表list、元组tuple、字典dict、集合set
    python3中没有long,只有无限精度的int
    7. 可变类型和不可变类型

    1. 可变类型有list,dict;不可变类型有string,number,tuple.
    2. 当进行修改操作时,可变类型传递的是内存中的地址,也就是说,直接修改内存中的值,并没有开辟新的内存。
    3. 不可变类型被改变时,并没有改变原内存地址中的值,而是开辟一块新的内存,将原地址中的值复制过去,对这块新开辟的内存中的值进行操作。
    4. python中的super()
      Python中的super(Net, self).init()是指首先找到Net的父类(比如是类NNet),
      然后把类Net的对象self转换为类NNet的对象,然后“被转换”的类NNet对象调用自己的init函数,
      其实简单理解就是子类把父类的__init__()放到自己的__init__()当中,
      这样子类就有了父类的__init__()的那些东西。

    super()如果不写参数的话,那么默认就是super(当前类, self)。
    9. Python中类方法、类实例方法、静态方法有何区别
    类方法:是类对象的方法,在定义时需要在上方使用@classmethod进行装饰,形参为cls,表示类对象,类对象和实例对象都可调用;
    类实例方法:是类实例化对象的方法,只有实例对象可以调用,形参为self,指代对象本身;
    静态方法:是一个任意函数,在其上方使用@staticmethod进行装饰,可以用对象直接调用,静态方法实际上跟该类没有太大关系;
    10. Python单例
    import threading
    class Singleton(object):
    _instance_lock = threading.Lock() # 如果不加锁,那么单例功能会有失效的情况。

    def __init__(self):
        pass
    
    def __new__(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"): # 如果该类“不“含有实例化对象
            with Singleton._instance_lock: 
                if not hasattr(Singleton, "_instance"): 
                    # 用父类(object)的new方法创建一个该类(cls==Singleton)的实例。
                    Singleton._instance = object.__new__(cls)  
        return Singleton._instance
    

    obj1 = Singleton()
    obj2 = Singleton()
    print(obj1,obj2)

    def task(arg):
    obj = Singleton()
    print(obj)

    for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()
    11. 请描述抽象类和接口类的区别和联系
    1.抽象类:规定了一系列的方法,并规定了必须由继承类实现的方法。由于有抽象方法的存在,所以抽象类不能实例化。
    可以将抽象类理解为毛坯房,门窗,墙面的样式由你自己来定,所以抽象类与作为基类的普通类的区别在于约束性更强。
    2.接口类:与抽象类很相似,表现在接口中定义的方法,必须由引用类实现,但他与抽象类的根本区别在于用途:
    与不同个体间沟通的规则,你要进宿舍需要有钥匙,这个钥匙就是你与宿舍的接口,你的舍友也有这个接口,
    所以他也能进入宿舍,你用手机通话,那么手机就是你与他人交流的接口。
    3.区别和关联:
    a.接口是抽象类的变体,接口中所有的方法都是抽象的,而抽象类中可以有非抽象方法,抽象类是声明方法的存在而不去实现它的类;
    b.接口可以继承,抽象类不行;
    c.接口定义方法,没有实现的代码,而抽象类可以实现部分方法;
    d.接口中基本数据类型为static而抽象类不是;
    12. 编写函数的4个原则
    1.函数设计要尽量短小;
    2.函数声明要做到合理、简单、易于使用;
    3.函数参数设计应该考虑向下兼容;
    4.一个函数只做一件事情,尽量保证函数语句粒度的一致性;
    13. 什么是lambda函数?有什么好处?
    lambda 函数是一个可以接收任意多个参数(包括可选参数)并且返回单个表达式值的函数;
    1.lambda函数比较轻便,即用即仍,很适合需要完成一项功能,但是此功能只在此一处使用,连名字都很随意的情况下;
    2.匿名函数,一般用来给filter,map这样的函数式编程服务;
    3.作为回调函数,传递给某些应用,比如消息处理;
    14. 什么是闭包
    在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。
    15. Python中的yield
    yield类似return会停止,但是不会结束。yield作为可迭代的 generator(生成器)对象,
    每次迭代的时候会在yield语句后停止。
    def simple_generator():
    x = 2
    yield x2
    yield x
    3
    yield x**4

    generator_object = simple_generator()
    [item for item in generator_object]
    #[4, 8, 16]
    16. Python实现私用变量的效果

    1. 单下划线表示的私有,但是依然可以直接访问;
    2. 双下划线表示的私有,因为被强制改名,所以不可以直接访问,但访问这个改成的变量名;
      class Rect:
      def init(self,area):
      self.__area = area
      @property
      def area(self):
      return self.__area
      rect = Rect(30)
      #直接通过方法名来访问 area 方法
      print(“矩形的面积是:”,rect.area)
      #假私有,_Rect__area就是__area新的变量名。
      rect._Rect__area
    3. 进程、线程、协程
      进程:程序运行在操作系统上的一个实例,就称之为进程。进程需要相应的系统资源:内存、时间片、pid。
      一个运行的程序就是一个进程,没有运行的代码叫程序。
      进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所有进程间数据不共享,且开销大。

    多进程:适合CPU密集操作(cpu指令比较多,如位多的浮点运算)。

    线程:cpu调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在。一个进程至少有一个线程,叫做主线程。
    而多个线程共享内存(数据共享,共享全局变量),从而极大的提高了程序的运行效率。

    多线程:适合IO密集性操作(读写数据操作比较多的,比如爬虫);

    协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。
    协程调度时,将寄存器上下文和栈保存到其它地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,
    直接操作栈基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

    三者关系:进程里面有线程,线程里面有协程。
    线程是并发,进程是并行。

    并行:同一时刻多个任务同时运行;
    并发:不会在同一时刻同时运行,存在交替执行的情况。
    18. Python异步的使用场景

    1. 不涉及共享资源,或者对于共享资源只读等非互斥操作;
    2. 没有时序上的严格关系;
    3. 不需要原子操作,或可以通过其它方式控制原子性;
    4. 常用于IO操作等耗时操作,因为比较影响客户体验和使用性能;
    5. 不影响主线程逻辑。
      所谓原子操作是指不会被 线程调度 机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。
    6. 什么是多线程竞争
      线程是非独立的,同一个进程里线程是数据共享的,当各个线程访问数据资源时会出现竞争状态,
      即:数据几乎同步被多个线程占有,造成数据混乱,即所谓的线程不安全。为此可添加锁。

    锁是Python提供的对线程控制的对象。有互斥锁,可重入锁,死锁。
    锁的好处:确保某段关键代码(共享数据资源)只能由一个线程从头到尾完整地执行;能解决资源竞争下的原子操作问题。
    锁的坏处:阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率大大下降。
    20. Python的线程同步

    1. setDaemon(False),当一个进程启动之后,会默认产生一个主线程,因为线程时程序执行的最小单位。
      当设置多线程时,主线程会创建多个子线程。在Python中,默认情况下就是setDaemon(False),
      主线程执行完自己的任务以后就退出。此时子线程会继续执行自己的任务,直到自己的任务结束。

    2. setDaemon(True),这时子线程为守护线程,主线程一旦执行结束,则全部子线程被强制终止。

    3. join(线程同步),join所完成的工作就是线程同步,即主线程任务结束以后,进入堵塞状态,
      一直等待所有的子线程结束以后,主线程再终止。
      当设置守护线程时,含义是主线程对于子线程等待timeout的时间将会杀死该子线程,最后退出程序。
      所以说,如果有10个子线程,全部等待时间就是每个timeout的累加和。
      如果没有设置守护线程时,主线程将会等待timeout的累加和这样的一段时间,时间一到,主线程结束,
      但是子线程依然存活,并且持续运行,直到子线程全部结束,程序退出。(孤儿进程)
      import threading
      import time

    def thread():
    time.sleep(2)
    print(’—子线程结束—’)

    def main():
    t1 = threading.Thread(target=thread)
    t1.setDaemon(True) # 设置子线程守护主线程
    t1.start()
    t1.join(timeout=1) # 线程同步,主线程堵塞1s,然后主线程结束,子线程继续执行;
    # 如果不设置timeout参数就等子线程结束后主线程再结束;
    # 如果设置了setDaemon=True和timeout=1主线程等待1s后会强制杀死子线程,然后主线程结束。
    print(’—主线程结束—’)

    main()
    21. 死锁
    定义:若干子线程在系统资源竞争的时候,都在等待对方对某部分资源解除占用状态,结果谁也不愿意先解锁,程序无法执行下去。
    GIL锁:全局解释器锁;
    作用:限制多线程同时执行,保证同一时间只有一个线程执行,所以cpython里的多线程其实是伪多线程。
    python里常常使用协程技术来代替多线程,协程是一种更轻量级的线程。
    进程和线程的切换是由系统决定,而协程由开发者决定,而模块gevent下切换是遇到了耗时操作时才会切换。
    22. 多线程交互访问数据,如何避免重读。
    创建一个已访问数据列表,用于存储已经访问过的数据,并加上互斥锁,
    在多线程访问数据的时候先查看数据是否在已访问的列表中,若存在就跳过。
    23. 线程安全、互斥锁
    每个对象都对应于一个可称为”互斥锁“的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
    同一进程中的多线程之间共享系统资源,多个线程同时对一个对象进行操作,一个线程操作尚未结束,另一线程已经对其进行操作,
    导致最终结果出现错误,此时需要对被操作对象添加互斥锁,保证每个线程对该对象的操作都得到正确的结果。
    24. 同步、异步、阻塞,非阻塞
    同步:多个任务之间有先后执行顺序,一个执行完下个才能执行。
    异步:多个任务之间没有先后顺序,可以同时执行,有时候一个任务可能要在必要的时候获取另一个同时执行的任务结果(回调操作)。
    阻塞:如果卡住了调用者,调用者不能继续往下执行,指的就是调用者发生阻塞。
    非阻塞:如果不会卡住,可以继续执行,指的就是非阻塞。
    同步、异步相对于多任务而言,阻塞非阻塞相对于代码执行而言。
    25. 什么是僵尸进程、孤儿进程
    孤儿进程:父进程退出,子进程还在运行的这些子进程都是孤儿进程,孤儿进程将被init进程(进程号为1)所收养,
    并由init进程对他们的完成状态进行收集。
    僵尸进程:进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait/waitpid获取子进程的状态信息,
    那么子进程的进程描述符仍然保存在系统中的这些进程。

    避免僵尸进程的方法:

    1. fork两次用孙子进程去完成子进程的任务;
    2. 用wait()函数使父进程阻塞;
    3. 使用信号量,在signal handler中调用waitpid,这样父进程不用阻塞。
    4. 多线程共同操作同一个数据互斥锁同步
      上锁解锁过程
      当一个线程调用锁的 acquire() 方法获得锁时,锁就进入“locked”状态。
      每次只有一个线程可以获得锁。
      如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,
      直到拥有锁的线程调用锁的 release() 方法释放锁之后,锁进入“unlocked”状态。
      线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。

    import threading
    import time

    g_num = 0

    def test1(num):
    global g_num
    for i in range(num):
    mutex.acquire() # 上锁
    g_num += 1
    mutex.release() # 解锁

    print("---test1---g_num=%d" % g_num)
    

    def test2(num):
    global g_num
    for i in range(num):
    mutex.acquire() # 上锁
    g_num += 1
    mutex.release() # 解锁

    print("---test2---g_num=%d" % g_num)