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
plko345
V2EX  ›  Python

关于编程思维方式, 想请教两个问题

  •  
  •   plko345 · 2020-12-03 23:08:40 +08:00 · 2619 次点击
    这是一个创建于 1428 天前的主题,其中的信息可能已经有所发展或是发生改变。

    问题一: 我在类里面有比较多的类似下面的写法, 但感觉非常不合适, 这种情况应该怎么去处理, 这里会多次的判断获取属性

    class Foo(object):
        def __init__(self):
            self.a = None
            self.b = None
    
        def get_a(self):
            if not self.a:
                self.a = xxxxx
    
        def get_b(self):
            if not self.b:
                self.b = xxxxx
    
        def do_sth1(self):
            self.get_a()        # 获取 self.a 属性
            # 使用 self.a
    
        def do_sth2(self):
            self.get_a()        # 获取 self.a 属性, 又一次
            self.get_b()        # 获取 self.b 属性
            # 使用 self.a 和 self.b
    

    问题二: 定义一个变量, type hint 指向一个自定义的类, 但这个变量我想为 空值(类似下面), 应该怎么写?

    class Bar(object):
        pass
    
    v: Bar = None   # 会警告不应该这么写
    
    a_list: list = []   # 正确的写法
    a_str: str = ""    # 正确的写法
    
    13 条回复    2020-12-04 22:27:44 +08:00
    ClericPy
        1
    ClericPy  
       2020-12-03 23:20:07 +08:00   ❤️ 1
    问题 1 里见过初始化 self._a 然后取的时候用 @property 来取, if not self._a: self._a = xxx 然后 return self._a
    比较常见于需要惰性初始化协程相关对象, 必须等 running loop 启动的时候保证 loop 一致. 或者其他惰性初始化的情况

    问题 2 的话, 我直接不让它提醒了

    "python.linting.mypyPath": "mypy",
    "python.linting.mypyArgs": [
    "--ignore-missing-imports",
    "--follow-imports=silent",
    "--show-column-numbers",
    "--no-strict-optional"
    ],
    Pagliacii
        2
    Pagliacii  
       2020-12-03 23:30:41 +08:00   ❤️ 1
    问题 2 可以这样写:

    ```python
    from typing import Optional
    v: Optional[Bar] = None
    ```
    plko345
        3
    plko345  
    OP
       2020-12-03 23:40:24 +08:00
    @ClericPy 谢谢指点, 还有关于问题一, 我是否从一开始类的设计上就存在问题, 因为我并没有使用到你说的 惰性初始化协程相关对象, 而且我写代码常常会这么写, 有时还会直接在 构造函数中 `self._a = self._get_a()` , 我感觉比较频繁这么使用是不对的
    plko345
        4
    plko345  
    OP
       2020-12-03 23:42:45 +08:00
    @Pagliacii 感谢, 这种写法像 rust 的 Option
    freakxx
        5
    freakxx  
       2020-12-03 23:59:03 +08:00
    @plko345 #3

    问题一,
    如果只是作为属性,并且不会经常改动,比如 set a,那么 init 就丢进去就好,
    或者像 1 楼说的,你改为 property 写法

    你这么写,感觉怪的地方在于你没显式返回,并且在 get 过程还做了 set 操作,显得不够清爽。

    问题二,
    我觉得这个写法是挺怪的,你不需要在这里声明,再赋值,
    或者说,你在这里声明 v,意义其实不大,反而会造成困扰。
    Pagliacii
        6
    Pagliacii  
       2020-12-04 00:04:33 +08:00
    @plko345 #3 `self.a = self._get_a()` 这样写的话,self.a 不就为 None 了吗?我看你的 self._get_a() 设置了 self.a 但是它没有返回值。

    另外,如果你想对属性赋值或取值时作出控制,你可以使用 property 这个装饰器,如下:
    ![image.png]( https://i.loli.net/2020/12/04/FxiKdVtsI9mZJ3R.png)
    plko345
        7
    plko345  
    OP
       2020-12-04 01:12:15 +08:00 via Android
    @freakxx 多谢建议,问题二我主要想用在函数参数里,不过用到的情况不多
    plko345
        8
    plko345  
    OP
       2020-12-04 01:14:29 +08:00 via Android
    @Pagliacii 是我没讲清楚,`self.a = self._get_a()` 这样写的时候是有 return 的
    zone10
        9
    zone10  
       2020-12-04 10:02:24 +08:00
    前面的已经说了, @property 和 optional, 你的 get 方法都没返回值怎么 get, 风格就不对了, 不太了解你的需求, 可能看一下 attrs 和 pydantic 这两个库能有所启发
    azcvcza
        10
    azcvcza  
       2020-12-04 10:04:42 +08:00
    要是 JS 的话,我喜欢这么做
    ```
    class foo{
    constructor(){
    this.data = {};
    this.functions = {};
    }

    get(key){
    return this.data[key] ? this.data[key] : undefined;
    }

    doSomething(key, data){
    if(this.functions[key]){
    this.functions[key](data);
    } else {
    throw new Error('function not exists');
    }
    }
    }

    var f = new foo();
    f.data = {a : 2};
    f.functions = {a: (data)=>{console.log(data)}}

    f.get(a);
    f.doSomething(a,'hello world');

    ```
    no1xsyzy
        11
    no1xsyzy  
       2020-12-04 11:14:29 +08:00
    functools.cached_property

    Backports: <https://pypi.org/project/backports.cached-property/>
    xpresslink
        12
    xpresslink  
       2020-12-04 17:53:14 +08:00
    (问题一)实在理解不了你的思维。如果 self.a/b 的初始值是固定的 xxxxx,直接放__init__里面就可以了。
    为什么要先在__init__赋个 None 然后再弄两个函数,在函数里面还多此一举检查有没有这个属性,分别再赋那个具体的值呢?直接写成下面这样不就行了么:
    class Foo(object):
    □□ def __init__(self):
    □□□□self.a = xxxxx
    □□□□self.b = xxxxx
    问题二:
    >>> v:Bar = None
    >>> v
    >>> b:Bar = Bar()
    >>> b
    <__main__.Bar object at 0x000001FC1AD36448>
    None 表示空值,它是一个特殊 Python 对象, None 的类型是 NoneType
    所以你说的这个变量我想为空值这个概念并不适合 Python 语言的变量。

    在 python 中变量并不同于 C++这类的静态语言,并不是容器的概念。
    在 python 中赋值过程是先执行等号右边语句创建对象,然后把对象地址赋给左边的“变量”,本质上变量只是个标签。
    plko345
        13
    plko345  
    OP
       2020-12-04 22:27:44 +08:00 via Android
    @xpresslink 问题一里,"xxxxx" 是一连串的过程,self.a 属性可能用到也可能用不到,用不到就没必要去执行 xxxxx 的过程,所以才有个 get 操作(经提醒应该改成 set),不过我也说了我觉得这么设计感觉不对。问题二我觉得二楼大大的方式比较符合我的需要,而且 Python 都有 type hint 了,也应该要考虑加入解决这个问题的方案,二楼的应该算是官方的一种解决方案了吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5800 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 06:27 · PVG 14:27 · LAX 23:27 · JFK 02:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.