企业项目管理、ORK、研发管理与敏捷开发工具平台

网站首页 > 精选文章 正文

哎?你的Python代码怎么这么像TypeScript

wudianyun 2025-06-12 16:48:40 精选文章 10 ℃

杂谈

同事和我在公司里都是全栈开发,前端都是使用Vue,后端为Python。

不过我在开发Vue时习惯JS,他更喜欢TS。好在我们自己的项目基本都是各自维护,倒也不会起很大的冲突。

有一天我的python代码被他看到了,就让他很奇怪:“你这python代码怎么这么像TS?”

我们来看看这串代码到底像不像:

from dataclasses import dataclass


@dataclass
class Dog:
    name: str
    age: int

再来看看如果是TS版本,应该是这个样子:

class Dog { 
    name: string; 
    age: number;
}

你别说,乍一看还是非常像的,但细心的朋友肯定看到python中多了一个 @dataclass,所以这其实是这个装饰器带来的效果。

一、@dataclass介绍

@dataclass 是python版本 3.7 以后作为内置方法进行引入的,旨在简化对象的初始化。

我们用以前方式初始化对象,可以看到使用 __init__ 方法初始化,这段代码重复性很强,而且还挺长:

class Dog:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

但现在 @dataclass 便简化了这一步骤,使得对象的初始化变得更加简洁且方便:

@dataclass
class Dog:
    name: str
    age: int

创建对象的时候与普通情况下保持一致,有两种方式:

dog1 = Dog('旺财', 10)
dog2 = Dog(name='二蛋', age=2)

二、创建默认值

当然,使用 @dataclass 亦可以创建一个默认值来确定属性:

@dataclass
class Dog:
    name: str = '二狗子'
    age: int = 10
    weight: float = 10.0
    is_health: bool = True

普通的类型默认值比较方便,但是像 列表、字典等 可变数据类型需要额外使用工厂方法来指定,否则会报错:

@dataclass
class Dog:
    ...
    info: dict = field(default_factory=lambda: {
        'color': 'black',
        'breed': 'Schnauzer'
    })
    children: list = field(default_factory=lambda: ['狗蛋', '小花', '奇奇'])

三、print打印

我们普通的对象使用 print() 打印输出时,一般长下面的样子:

<__main__.Dog object at 0x00000282D3CBE280>

除非你实现了 __str__ 或者 __repr__ 才能显示对象的内容,但如果你的对象是通过 @dataclass 的类创建的,那么将自动实现 __repr__print() 输出时如下:

dog = Dog()
# Dog(name='二狗子', age=10, weight=10.0, is_health=True, info={'color': 'black', 'breed': 'Schnauzer'}, children=['狗蛋', '小花', '奇奇'])
print(dog)  

这样对象的打印输出就非常方便了,如果你不想在 print() 时显示一些属性,可以使用 repr=False 来隐藏:

@dataclass
class Dog:
    name: str = '二狗子'
    age: int = 10
    weight: float = 10.0
    is_health: bool = True
    info: dict = field(default_factory=lambda: {
        'color': 'black',
        'breed': 'Schnauzer'
    }, repr=False)
    children: list = field(default_factory=lambda: ['狗蛋', '小花', '奇奇'], repr=False)


dog = Dog()
# Dog(name='二狗子', age=10, weight=10.0, is_health=True)
print(dog)

四、只读

有的时候,我们某些对象是用来控制配置信息的,这些对象属性不能被修改,因此改为只读,可以如下操作:

@dataclass(frozen=True)
class Config:
    ip: str = '192.168.2.54'
    port: int = 8000

五、强转

通过 @dataclass 修饰的类所创建的对象,可以强转为相应的字典或者元祖,代码如下:

from dataclasses import asdict, astuple

dog = Dog()
# {'name': '二狗子', 'age': 10, 'weight': 10.0, 'is_health': True, 'info': {'color': 'black', 'breed': 'Schnauzer'}, 'children': ['狗蛋', '小花', '奇奇']}
print(asdict(dog))
# ('二狗子', 10, 10.0, True, {'color': 'black', 'breed': 'Schnauzer'}, ['狗蛋', '小花', '奇奇'])
print(astuple(dog))

六、与__init__的冲突

如果我们在 @dataclass 修饰下的类下面添加 __init__ 初始化方法会有什么影响呢?

当你的 __init__ 方法没有填写任何参数,那么你对象初始化时就无法对属性进行修改:

@dataclass()
class Dog:
    name: str = '二狗子'
    age: int = 10
    weight: float = 10.0
    is_health: bool = True

    def __init__(self):
        pass


dog1 = Dog('测试')  # 报错
dog2 = Dog(name='测试') # 报错

想要修改相应的属性,添加上参数即可:

@dataclass()
class Dog:
    name: str = '二狗子'
    age: int = 10
    weight: float = 10.0
    is_health: bool = True

    def __init__(self, name='', age=10):
        self.name = name
        self.age = age


dog1 = Dog('测试')
dog2 = Dog(name='测试')

注意:未添加的参数还是无法修改的,如 weight、is_health

七、结尾

我们可以通过 @dataclass 方便快捷地创建一个类,只是要在 __init__ 也存在的情况下注意使用规则。

如果你也是重度面向对象使用者,那么这个方法特别适合你!

Tags:

最近发表
标签列表