V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
danzzzz
V2EX  ›  Python

请问通过 dict.get(xxxx,'None')这种操作获得字典中的数据的时候,为什么会出现时有时无的情况?

  •  
  •   danzzzz · 2018-10-25 13:12:51 +08:00 · 3560 次点击
    这是一个创建于 2255 天前的主题,其中的信息可能已经有所发展或是发生改变。

    就是我一开始定义了一个 session = {},紧接着通过 session['a'] = 1 的方式给字典赋值, 然后在 get 的过程中,出现了有时候拿到了数据,有时候返回 None 的情况,不得其解。 麻烦大家了,先感谢各位的解答。

    第 1 条附言  ·  2018-10-25 15:28:42 +08:00
    global session
    session = {}

    class Todolist(Resource):
    #用户进入页面后将该用户所有的待办事项返回给前端渲染到页面
    def get(self,rdSession):
    global session
    openid = session.get(rdSession,None) #通过前端发送过来的随机串 key 得到对应的 value
    user = User.query.filter_by(open_id=openid).first()
    if user is None:
    return jsonify({'err':'This user_id is not in the user_table!'})
    pass

    #增
    def post(self,rdSession):
    '''
    获取用户的 open_id 的目的是在添加事项的时候给事项记录加上 user_id,
    因为 To_do_list 表中的 user_id 是 User 表的外键,
    这样才能实现通过 user.to_do_lists 来获取用户对应的所有待办事项。
    '''
    global session
    openid = session.get(rdSession,None)
    user = User.query.filter_by(open_id=openid).first()
    if user is None:
    return jsonify({'err':'This user_id is not in the user_table!'})
    pass

    #前端扫码向服务器发送临时 code
    class Session3d(Resource):
    def get(self):
    global session
    appid = 'xxxxxx'
    secrect = 'xxxxxxxxxxx'
    js_code = request.args.get('code',None)
    if js_code is not None:
    url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' + appid + '&secret=' + secrect + '&js_code=' + js_code + '&grant_type=authorization_code'
    r = requests.get(url)
    json_data = r.json()
    openid = json_data.get('openid')

    tupl = get_3rd_session(openid) #对 openid 进行加密
    rdSession = tupl[1].decode() #得到加密后的随机串

    user = User(open_id=openid)
    db.session.add(user)
    db.session.commit()
    session[rdSession] = openid #将随机串和 openid 作为键值对存进 session
    return jsonify(rdSession=rdSession) #返回随机串给前端
    return jsonify({})

    api.add_resource(Todolist,'/v1_todolist/<rdSession>')
    api.add_resource(Session3d,'/get_session3d')
    24 条回复    2018-10-26 09:46:08 +08:00
    Rand01ph
        1
    Rand01ph  
       2018-10-25 13:28:20 +08:00
    有代码吗?
    hand515
        2
    hand515  
       2018-10-25 13:43:47 +08:00
    >>> s ={}
    >>> s['a']=None
    >>> s['b']=1
    >>> s.get('a','None')
    >>> print s.get('a','None2')
    None
    >>> print s.get('b','None2')
    1
    >>> print s.get('c','None2')
    None2
    jasonqiao36
        3
    jasonqiao36  
       2018-10-25 13:51:16 +08:00 via Android
    "None" --> None
    danzzzz
        4
    danzzzz  
    OP
       2018-10-25 13:53:07 +08:00
    @Rand01ph
    这是类的外部定义的
    global session
    session = {}

    紧接着
    类的内部的某个函数
    global session
    openid = json_data.get('openid','None')
    tupl = get_3rd_session(openid)
    rdSession = tupl[1].decode()
    session[rdSession] = openid #看这一句跟第一句就好了

    最后在另外一个类的某个函数里面调用的时候是这样的
    global session
    openid = session.get(rdSession,'None')

    理论上讲应该是能获取到这个 openid 的,但是却出现了有时拿到了有时拿不到的情况。百思不得其解
    anonymous256
        5
    anonymous256  
       2018-10-25 13:54:30 +08:00
    session = {'a': 1, 'b': 2, 'c': 3}
    session.get('a') 这种, 只要字典中有这个 key 是可以拿到相应的 value, 如果返回是 None, 则说明这个 key 不存在. 显然, 你"有时候返回 None 的情况", 就是因为不存在这个 key. 一般你可以对 None 这种情况进行处理, 不妨这样写:


    value = session.get('某个 key')
    if value is not None:
    # 获取成功 do something
    else:
    # 该 key 不存在
    anonymous256
        6
    anonymous256  
       2018-10-25 14:02:44 +08:00
    @danzzzz #4
    1. 建议你在赋值之前 print 这两个: openid, rdSession 键值对, 确定你的 rdSession 存在于这个 dict 中.
    2. 大量重复使用 global session, 似乎不是一个好方法, 如果它不是可变的对象, 建议在类的构造方法中这样写:
    def __init__(self, session)
    ....self.session = session
    danzzzz
        7
    danzzzz  
    OP
       2018-10-25 14:03:56 +08:00
    @anonymous256 谢谢你的回答。不过我确实是把 key 以及对应的 value 加进去了,但是在不断的获取的过程中出现了有时候这个 key 存在,有时候又不存在的情况,我不太清楚是什么原因。
    danzzzz
        8
    danzzzz  
    OP
       2018-10-25 14:09:38 +08:00
    @anonymous256
    其实我是最近在写一个微信小程序的项目,在登录的时候需要通过一个随机串来判断对应的 openid 是否存在,这个 rdSession 就是随机串,跟 openid 形成键值对。
    我在每次将 rdSession 返回给前端的时候,都会将其写入 session 中,之后调用接口的时候就利用 rdSession 来判断是谁调用了接口,再返回数据。
    最后就是卡在了这个地方,每次 session.get()来寻找是哪个用户的时候,有时候就找不到了,有时候却可以,很是纳闷。
    danzzzz
        9
    danzzzz  
    OP
       2018-10-25 14:11:58 +08:00
    @jasonqiao36 不好意思,不太明白你的意思。
    anonymous256
        10
    anonymous256  
       2018-10-25 14:14:05 +08:00
    @danzzzz #7 建议采用 logging 跟踪一下键值对的变化. dict 作为 python 最强大的数据结构之一, 应该不可能出现存在这个 key 却获取不到的情况, 我觉得唯一的可能是你对这个 key 最后一次的赋值了 None.
    d = {}
    d['1'] = 1
    d['1'] = None
    print(d.get('1'))
    Sylv
        11
    Sylv  
       2018-10-25 14:17:30 +08:00 via iPhone
    可能性一:存取的 session 不是同一个 session。
    可能性二:存取的键 rdSession 不是同一个值。

    试着在每次 session 存取时:
    print(id(session)
    print(repr(rdSession))
    检查是否一致。
    anonymous256
        12
    anonymous256  
       2018-10-25 14:18:47 +08:00
    @danzzzz #9
    他的意思是这个:
    session = {'a': 1}
    b1 = session.get('b1', 'None')
    b2 = session.get('b2', None)

    >>>print(b1, b2)
    None None
    >>>print(type(b1), type(b2))
    <class 'str'> <class 'NoneType'>
    Hstar
        13
    Hstar  
       2018-10-25 14:29:14 +08:00
    推测,在函数获取前又走了一遍
    ``` python
    global session
    session = {}
    ```
    建议追踪一个这个 session 变量
    mengzhuo
        14
    mengzhuo  
       2018-10-25 15:03:50 +08:00
    恭喜你,有时有,有时无,大概率是数据竞态。
    单节点可以通过减少全局变量,读写锁来规避。
    多节点的话,用 memcache、redis 之类的保证。
    集群的话,shard+version (乐观锁)能基本保证。
    再向上我没有碰到过了,欢迎讨论。
    Yourshell
        15
    Yourshell  
       2018-10-25 15:19:47 +08:00 via iPhone
    你要不每次 get 之前先 print 出来吧,现在我们也不知道你的上下文,你说的只需要看某一段代码有点不妥。
    danzzzz
        16
    danzzzz  
    OP
       2018-10-25 15:23:02 +08:00
    @anonymous256 上网查了一些相关资料,不是很明白怎么跟踪。不知道前辈能不能讲一下大概。
    danzzzz
        17
    danzzzz  
    OP
       2018-10-25 15:29:40 +08:00
    @Yourshell 我把相关代码都贴上了,劳烦看一下了。
    danzzzz
        18
    danzzzz  
    OP
       2018-10-25 15:30:09 +08:00
    @anonymous256 不好意思,我把部分相关的代码贴上了,能麻烦你看一下吗?
    Rand01ph
        19
    Rand01ph  
       2018-10-25 15:47:33 +08:00
    @danzzzz 那你这个服务是如何 run 起来的呢?看起来有可能是多进程模型的问题。
    anonymous256
        20
    anonymous256  
       2018-10-25 16:04:46 +08:00
    @danzzzz
    1. 跟踪这个问题的关键是, 输出的这个字典, 我观察到:
    openid = session.get(rdSession,None) # Todolist 类, get 方法中, 字典的访问操作 F1
    openid = session.get(rdSession,None) # Todolist 类, post 方法中, 字典的访问操作 F2
    session[rdSession] = openid # Session3d 类中, get 方法中, 字典的修改操作 F3

    因此你要在这个三个地方, 输出这个字典. 尤其是修改操作的前后的变化, 要跟踪其中的值.

    2. 追踪这个 dict 的值有两种方式, 如果你是开发环境, 可以直接 print 出来, 或者用 logging 模块. 下面是:
    logging 的方式:

    # Logging 配置
    logger = logging.getLogger('my_logger')
    logger.setLevel(logging.INFO)
    file_handler = logging.FileHandler(filename='我的小程序日志.log')
    formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] ' +
    '%(levelname)s %(message)s')
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)

    然后在 F1, F2 的地方: logging.info(str(session)),
    在 F3 的代码的上下行 : logging.info(str(session)),

    3. Todolist 中只有字典的访问的操作, 似乎不需要使用 global 关键字也可以
    danzzzz
        21
    danzzzz  
    OP
       2018-10-25 17:35:15 +08:00
    @Rand01ph 这个是放在服务器上跑的。
    danzzzz
        22
    danzzzz  
    OP
       2018-10-25 17:36:56 +08:00
    @anonymous256 不好意思,请问放到代码中的时候是这样子吗?
    '''
    ....上面的代码省略

    import logging

    logger = logging.getLogger('my_logger')
    logger.setLevel(logging.INFO)
    file_handler = logging.FileHandler(filename='我的小程序日志.log')
    formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] ' +
    '%(levelname)s %(message)s')
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)

    class Todolist(Resource):
    #用户进入页面后将该用户所有的待办事项返回给前端渲染到页面
    def get(self,rdSession):
    global session
    logging.info(str(session))
    openid = session.get(rdSession,None) #通过前端发送过来的随机串 key 得到对应的 value
    logging.info(str(session))
    xpresslink
        23
    xpresslink  
       2018-10-25 17:46:54 +08:00
    请问楼主这个代码是多进程方式部署的么?
    重要的事情说三遍:
    每个进程中的变量是相互隔离的。
    每个进程中的变量是相互隔离的。
    每个进程中的变量是相互隔离的。

    进程是并发执行的,进程之间默认是不能共享全局变量的。如果要共享全局变量需要用
    ( multiprocessing.Value("d",10.0),数值)
    ( multiprocessing.Array("i",[1,2,3,4,5]),数组)
    ( multiprocessing.Manager().dict(),字典)
    ( multiprocessing.Manager().list(range(5)))。
    进程通信(进程之间传递数据)用进程队列( multiprocessing.Queue(),单向通信),
    管道( multiprocessing.Pipe() ,双向通信)。

    或者用个 redis 服务来保存 kv
    vipppppp
        24
    vipppppp  
       2018-10-26 09:46:08 +08:00
    这是个 flask 服务吗 ==
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2093 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 00:22 · PVG 08:22 · LAX 16:22 · JFK 19:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.