,

Python类属性差异

类属性 所有对象共享的属性,在类内部直接定义: 访问规则: 实例属性通过 self 访问,类属性通过 类名 或…

类属性

所有对象共享的属性,在类内部直接定义:

class Dog:
   species = "Canis familiaris"  # 类属性

   def __init__(self, name):
       self.name = name

dog1 = Dog("Buddy")
dog2 = Dog("Max")

print(dog1.species)  # Canis familiaris
print(dog2.species)  # Canis familiaris

# 修改类属性(通过类名)
Dog.species = "Canis lupus"
print(dog1.species)  # Canis lupus

访问规则: 实例属性通过 self 访问,类属性通过 类名self 访问。

class Person:
    country = "中国"  # 类属性(存在类空间 Person.__dict__)
    def __init__(self, name):
        self.name = name  # 实例属性(存在实例空间 p.__dict__)

p = Person("张三")
  1. 类属性:属于类本身,所有实例共享一份数据
  2. 实例属性:每个对象自己独有

Python 规则:对象 . 访问属性时,查找顺序

对象.属性名 查找流程(就近原则):

  1. 先去实例自己的字典 obj.__dict__ 找;
  2. 找不到 → 去所属类 cls.__dict__ 找类属性;
  3. 还找不到 → 顺着继承 MRO 往上父类依次查找。

所以 p.country:实例没有 country,自动去 Person 里取类属性,语法合法。

对比 Java/C++ 为什么不能用对象访问静态变量(类属性)

Java

class Person {
    static String country = "中国"; // 静态变量=Python类属性
}
public class Test {
    public static void main(String[] args) {
        Person p = new Person();
        // 语法允许编译通过,但编码规范强制禁止,IDE会警告
        System.out.println(p.country); 
        // 标准写法:Person.country
    }
}
  1. 语法层面:Java 其实能对象。静态变量,只是规范不推荐;
  2. 设计思想:静态属于类,实例只是附属载体,为了代码可读性强制要求类名调用,避免混淆实例 / 静态;
  3. 底层:编译期 p.country 会被编译器优化成 Person.country,对象引用完全没用。

C++

同理,对象可以访问静态成员,但业界统一只用 类名::静态变量,目的区分层级。

Python 独特设计:动态命名空间分离,读写行为不一样

1. 读取:对象。类属性 可读(共享)

print(p.country)   # 读:实例没有,去类拿
print(Person.country)

2. 赋值:对象。类属性 会新增实例属性,不会修改类属性(大坑)

p.country = "美国"  
# 不会改 Person.country!而是给实例p新增实例属性country

print(p.country)      # 美国(实例自己的)
print(Person.country) # 中国(类属性没变)

p2 = Person("李四")
print(p2.country)     # 中国,不受p影响

真正修改类属性必须用类名:

Person.country = "华夏"

Python 允许对象访问类属性的底层 & 设计原因

  1. 统一 . 访问语法,简化 API Python 追求语法统一,不管实例属性 / 类属性,全部用 . 访问,不用区分两种调用语法,降低学习成本。
  2. 属性查找是动态链式查询(MRO 统一机制) Python 没有给 “类属性” 单独一套访问语法,统一走一套属性检索链路:实例→类→父类,天然支持对象读取类属性。
  3. 动态语言无编译期校验,不强制语法隔离 Java/C++ 是静态编译语言,编译时就能区分静态 / 实例,强制规范; Python 运行时才查找属性,没有编译阶段做语法拦截,自然放开读取权限。
  4. 实用场景:实例直接读取共享配置 类属性常用来存全局配置、常量、计数器,实例不需要写 类名.xxx,直接 self.xxx 就能读取共享数据,代码更简洁:
class Request:
    timeout = 30  # 全局超时配置
    def send(self):
        print(f"请求超时 {self.timeout}") # 直接self读类属性

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

About the Author

每个人都有自己得时区,在自己得时区里,一切都是准时的。

BlockSpare — News, Magazine and Blog Addons for (Gutenberg) Block Editor