Hướng dẫn static variable python - python biến tĩnh

Class variable (biến thuộc về lớp) hoặc static variable (biến tĩnh) đều là các biến được chia sẻ cho tất cả các đối tượng của lớp. Còn Instance variable (biến thuộc về thể hiện của lớp) hoặc non-static variable (biến không tĩnh) thì là khác nhau giữa các đối tượng khác nhau (mỗi đối tượng đều có một bản sao của các instance variable và non-static variable của lớp).

 Ví dụ, giả sử ta có một Computer Science Student (sinh viên ngành khoa học máy tính) thuộc lớp CSStudent. Lớp này có thể có một biến tĩnh mang giá trị “cse” cho tất cả các đối tượng. Và lớp này cũng có thể có các biến thành viên không tĩnh như là name và roll.

Trong C++ và Java, chúng ta có thể sử dụng từ khóa static để làm cho một biến bình thường trở thành một class variable (biến thuộc về class). Những biến mà không có  từ khóa static đặt ở trước thì đều là các instance variable (tức là biến thuộc về đối tượng/thể hiện của lớp đó). 

 Cách tiếp cận trong Python thì tương đối đơn giản, nó không yêu cầu từ khóa static. Những biến mà được gán cho một giá trị ở bên trong phần code khai báo của lớp thì đều là class variable – biến thuộc về lớp. Và những biến mà được gán cho các giá trị ở bên trong các phương thức thì đều là instance variable – biến thuộc về thể hiện/đối tượng của lớp.

Ví dụ mô tả những biến được gán cho một giá trị ở bên trong phần code khai báo của lớp thì đều là class variable – biến thuộc về lớp:

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: 
#Fanpage: https://www.facebook.com/cafedevn
#Instagram: https://instagram.com/cafedevn
#Twitter: https://twitter.com/CafedeVn
#Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
# -----------------------------------------------------------


# Python program to show that the variables with a value  
# assigned in class declaration, are class variables 
  
# Class for Computer Science Student 
class CSStudent: 
    stream = 'cse'                  # Class Variable 
    def __init__(self,name,roll): 
        self.name = name            # Instance Variable 
        self.roll = roll            # Instance Variable 
  
# Objects of CSStudent class 
a = CSStudent('Cafedev', 1) 
b = CSStudent('Nerd', 2) 
  
print(a.stream)  # prints "cse" 
print(b.stream)  # prints "cse" 
print(a.name)    # prints "Geek" 
print(b.name)    # prints "Nerd" 
print(a.roll)    # prints "1" 
print(b.roll)    # prints "2" 
  
# Class variables can be accessed using class 
# name also 
print(CSStudent.stream) # prints "cse" 

Kết quả in ra là:

cse
cse
Cafedev
Nerd
1
2
cse

Nguồn và Tài liệu tiếng anh tham khảo:

  • w3school
  • python.org
  • geeksforgeeks

Tài liệu từ cafedev:

  • Full series tự học Python từ cơ bản tới nâng cao tại đây nha.
  • Ebook về python tại đây.
  • Các series tự học lập trình khác

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

  • Group Facebook
  • Fanpage
  • Youtube
  • Instagram
  • Twitter
  • Linkedin
  • Pinterest
  • Trang chủ

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!

Phương pháp tĩnh và lớp học

Như các câu trả lời khác đã lưu ý, các phương pháp tĩnh và lớp dễ dàng được thực hiện bằng cách sử dụng các nhà trang trí tích hợp:

Nội dung chính

  • Phương pháp tĩnh và lớp học
  • "Biến tĩnh"
  • "Biến tĩnh" bất biến "
  • Một gotcha để nhận thức được
  • Các biến tĩnh thực tế, thực tế - x1 = Test() x2 = Test() x1.i = 50 assert x2.i == x1.i # no error assert x2.i == 50 # the property is synced 9

class Test(object):

    # regular instance method:
    def my_method(self):
        pass

    # class method:
    @classmethod
    def my_class_method(cls):
        pass

    # static method:
    @staticmethod
    def my_static_method():
        pass

Như thường lệ, đối số đầu tiên cho

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

2 bị ràng buộc với đối tượng thể hiện lớp. Ngược lại, đối số đầu tiên với
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
3 bị ràng buộc với chính đối tượng lớp (ví dụ: trong trường hợp này,
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
4). Đối với
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
5, không có đối số nào bị ràng buộc và có các đối số là tùy chọn.

"Biến tĩnh"

"Biến tĩnh" bất biến "

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

Một gotcha để nhận thức được

Các biến tĩnh thực tế, thực tế - x1 = Test() x2 = Test() x1.i = 50 assert x2.i == x1.i # no error assert x2.i == 50 # the property is synced 9

Như thường lệ, đối số đầu tiên cho not with the class itself; see "gotcha" below), can be achieved by turning the class attribute into a property:

cse
cse
Cafedev
Nerd
1
2
cse
3

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

2 bị ràng buộc với đối tượng thể hiện lớp. Ngược lại, đối số đầu tiên với
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
3 bị ràng buộc với chính đối tượng lớp (ví dụ: trong trường hợp này,
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
4). Đối với
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
5, không có đối số nào bị ràng buộc và có các đối số là tùy chọn.

cse
cse
Cafedev
Nerd
1
2
cse
4

Tuy nhiên, việc thực hiện "các biến tĩnh" (tốt, các biến tĩnh có thể thay đổi, dù sao đi nữa, nếu đó không phải là một mâu thuẫn trong các điều khoản ...) không phải là thẳng về phía trước. Như Millerdev đã chỉ ra trong câu trả lời của mình, vấn đề là các thuộc tính lớp của Python không thực sự là "biến tĩnh". Xem xét:

Điều này là do dòng

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
6 đã thêm một thuộc tính thể hiện mới
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
7 vào
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
8 thay vì thay đổi giá trị của thuộc tính
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
4
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
7.

Một phần hành vi biến tĩnh dự kiến, tức là, đồng bộ hóa thuộc tính giữa nhiều trường hợp (nhưng không phải với chính lớp; xem "Gotcha" bên dưới), có thể đạt được bằng cách biến thuộc tính lớp thành thuộc tính:not with the class itself; see "gotcha" below), can be achieved by turning the class attribute into a property:

"Biến tĩnh" bất biến "

Một gotcha để nhận thức được

Các biến tĩnh thực tế, thực tế - x1 = Test() x2 = Test() x1.i = 50 assert x2.i == x1.i # no error assert x2.i == 50 # the property is synced 9

Như thường lệ, đối số đầu tiên cho

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

2 bị ràng buộc với đối tượng thể hiện lớp. Ngược lại, đối số đầu tiên với
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
3 bị ràng buộc với chính đối tượng lớp (ví dụ: trong trường hợp này,
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
4). Đối với
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
5, không có đối số nào bị ràng buộc và có các đối số là tùy chọn.

Một gotcha để nhận thức được

Các biến tĩnh thực tế, thực tế - x1 = Test() x2 = Test() x1.i = 50 assert x2.i == x1.i # no error assert x2.i == 50 # the property is synced 9not work when using the class itself. So for example:

class Test(object):

    # regular instance method:
    def my_method(self):
        pass

    # class method:
    @classmethod
    def my_class_method(cls):
        pass

    # static method:
    @staticmethod
    def my_static_method():
        pass
3

Như thường lệ, đối số đầu tiên cho

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

2 bị ràng buộc với đối tượng thể hiện lớp. Ngược lại, đối số đầu tiên với
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
3 bị ràng buộc với chính đối tượng lớp (ví dụ: trong trường hợp này,
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
4). Đối với
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
5, không có đối số nào bị ràng buộc và có các đối số là tùy chọn.

Tuy nhiên, việc thực hiện "các biến tĩnh" (tốt, các biến tĩnh có thể thay đổi, dù sao đi nữa, nếu đó không phải là một mâu thuẫn trong các điều khoản ...) không phải là thẳng về phía trước. Như Millerdev đã chỉ ra trong câu trả lời của mình, vấn đề là các thuộc tính lớp của Python không thực sự là "biến tĩnh". Xem xét:

Điều này là do dòng

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
6 đã thêm một thuộc tính thể hiện mới
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
7 vào
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
8 thay vì thay đổi giá trị của thuộc tính
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
4
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
7.

Một phần hành vi biến tĩnh dự kiến, tức là, đồng bộ hóa thuộc tính giữa nhiều trường hợp (nhưng không phải với chính lớp; xem "Gotcha" bên dưới), có thể đạt được bằng cách biến thuộc tính lớp thành thuộc tính:not with the class itself; see "gotcha" below), can be achieved by turning the class attribute into a property:

Bây giờ bạn có thể làm:

Biến tĩnh bây giờ sẽ vẫn đồng bộ giữa tất cả các trường hợp lớp.

.

Lưu ý rằng về mặt kỹ thuật,

Các biến tĩnh thực tế, thực tế - x1 = Test() x2 = Test() x1.i = 50 assert x2.i == x1.i # no error assert x2.i == 50 # the property is synced 9

Như thường lệ, đối số đầu tiên cho

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

2 bị ràng buộc với đối tượng thể hiện lớp. Ngược lại, đối số đầu tiên với
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
3 bị ràng buộc với chính đối tượng lớp (ví dụ: trong trường hợp này,
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
4). Đối với
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
5, không có đối số nào bị ràng buộc và có các đối số là tùy chọn.is really pretty awful; if you insist on doing something like this (hint: please don't; Python is a very elegant language and shoe-horning it into behaving like another language is just not necessary), use the code in Ethan Furman's answer instead.

Tuy nhiên, việc thực hiện "các biến tĩnh" (tốt, các biến tĩnh có thể thay đổi, dù sao đi nữa, nếu đó không phải là một mâu thuẫn trong các điều khoản ...) không phải là thẳng về phía trước. Như Millerdev đã chỉ ra trong câu trả lời của mình, vấn đề là các thuộc tính lớp của Python không thực sự là "biến tĩnh". Xem xét:

Điều này là do dòng

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
6 đã thêm một thuộc tính thể hiện mới
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
7 vào
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
8 thay vì thay đổi giá trị của thuộc tính
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
4
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
7.

Tuy nhiên, bạn có thể xác định metaclass của riêng bạn như thế này:

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

8

Và áp dụng nó cho lớp của riêng bạn như thế này (chỉ Python 3):

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
0

Dưới đây là một metaclass mà tôi đã tạo ra để cố gắng mô phỏng hành vi "biến tĩnh" của các ngôn ngữ khác. Về cơ bản, nó hoạt động bằng cách thay thế bộ getter, setter và deleter mặc định bằng các phiên bản kiểm tra xem có yêu cầu thuộc tính có phải là "biến tĩnh" không.

Một danh mục của "biến tĩnh" được lưu trữ trong thuộc tính

cse
cse
Cafedev
Nerd
1
2
cse
91. Tất cả các yêu cầu thuộc tính ban đầu được cố gắng để được giải quyết bằng cách sử dụng thứ tự độ phân giải thay thế. Tôi đã đặt tên là "thứ tự độ phân giải tĩnh" hoặc "sro". Điều này được thực hiện bằng cách tìm kiếm thuộc tính được yêu cầu trong tập hợp "biến tĩnh" cho một lớp nhất định (hoặc các lớp cha của nó). Nếu thuộc tính không xuất hiện trong "SRO", lớp sẽ quay trở lại trên hành vi GET/SET/SET/Xóa thuộc tính mặc định (nghĩa là "MRO").
class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i
1