Tại sao __init __ () luôn được gọi sau __new __ ()?


568

Tôi chỉ đang cố gắng hợp lý hóa một trong các lớp học của mình và đã giới thiệu một số chức năng theo phong cách tương tự như mẫu thiết kế bay bổng .

Tuy nhiên, tôi hơi bối rối về lý do tại sao __init__luôn được gọi sau __new__. Tôi đã không mong đợi điều này. Bất cứ ai có thể cho tôi biết tại sao điều này xảy ra và làm thế nào tôi có thể thực hiện chức năng này nếu không? (Ngoài việc đưa việc triển khai vào __new__đó mà cảm thấy khá khó khăn.)

Đây là một ví dụ:

class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print "EXISTS"
            return A._dict['key']
        else:
            print "NEW"
            return super(A, cls).__new__(cls)

    def __init__(self):
        print "INIT"
        A._dict['key'] = self
        print ""

a1 = A()
a2 = A()
a3 = A()

Đầu ra:

NEW
INIT

EXISTS
INIT

EXISTS
INIT

Tại sao?


cũng đã cố gắng để hiểu mẫu thiết kế, và lần đầu tiên nghe nói về: mẫu thiết kế fly trọng .. và liên kết rất tốt có ví dụ trong hầu hết tất cả các langugages phổ biến.
EmmaYang

Câu trả lời:


598

Sử dụng __new__khi bạn cần kiểm soát việc tạo một thể hiện mới.

Sử dụng __init__khi bạn cần kiểm soát việc khởi tạo một thể hiện mới.

__new__là bước đầu tiên của việc tạo cá thể. Nó được gọi đầu tiên và chịu trách nhiệm trả về một thể hiện mới của lớp bạn.

Ngược lại, __init__không trả lại bất cứ điều gì; nó chỉ chịu trách nhiệm khởi tạo thể hiện sau khi nó được tạo.

Nói chung, bạn không cần ghi đè __new__trừ khi bạn phân lớp một loại không thay đổi như str, int, unicode hoặc tuple.

Từ tháng 4 năm 2008 bài: Khi nào sử dụng __new__so với __init__? trên mail.python.org.

Bạn nên xem xét rằng những gì bạn đang cố gắng thường được thực hiện với Nhà máy và đó là cách tốt nhất để làm điều đó. Sử dụng __new__không phải là một giải pháp sạch tốt, vì vậy hãy xem xét việc sử dụng của một nhà máy. Ở đây bạn có một ví dụ nhà máy tốt .


1
Đã kết thúc bằng cách sử dụng __new__bên trong một lớp Factory, nó đã trở nên khá sạch sẽ, vì vậy cảm ơn bạn đã đóng góp.
Dan

11
Xin lỗi, tôi không đồng ý rằng việc sử dụng __new__nên được giới hạn nghiêm ngặt trong các trường hợp được nêu. Tôi đã thấy nó rất hữu ích để triển khai các nhà máy lớp chung có thể mở rộng - xem câu trả lời của tôi cho câu hỏi Sử dụng không đúng __new__cách để tạo các lớp trong Python? cho một ví dụ về làm như vậy.
martineau 3/2/2015

170

__new__là phương thức lớp tĩnh, trong khi __init__là phương thức thể hiện. __new__phải tạo cá thể trước, để __init__có thể khởi tạo nó. Lưu ý rằng __init__lấy selflàm tham số. Cho đến khi bạn tạo cá thể thì không có self.

Bây giờ, tôi tập hợp, rằng bạn đang cố gắng triển khai mẫu singleton trong Python. Có một vài cách để làm điều đó.

Ngoài ra, kể từ Python 2.6, bạn có thể sử dụng các trình trang trí lớp .

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...

8
@Tyler Lâu rồi, tôi không hiểu "@singleton" hoạt động như thế nào? Bởi vì trình trang trí trả về một hàm nhưng MyClass là một lớp.
Alcott

11
Tại sao lại là từ điển? Vì cls sẽ luôn giống nhau và bạn lấy một từ điển mới cho mỗi singleton bạn đang tạo một từ điển chỉ có một mục trong đó.
Winston Ewert

7
@ Alcott: Không cần ý kiến ​​- các tài liệu đồng ý với bạn.
Ethan Furman

2
@ Hủy bỏ. có trang trí trả về một chức năng. nhưng cả lớp và hàm đều có thể gọi được. Tôi nghĩ rằng các trường hợp = {} nên là một biến toàn cục.
Tyler Long

4
@TylerLong Alcott có một điểm tốt. Điều này dẫn đến tên MyClassbị ràng buộc với một hàm trả về các thể hiện của lớp gốc. Nhưng hiện tại không có cách nào để tham khảo lớp gốc, và MyClasslà một hàm phá vỡ isinstance/ issubclasskiểm tra, truy cập trực tiếp vào các thuộc tính / phương thức của lớp như MyClass.something, đặt tên lớp trong supercác cuộc gọi, v.v., cho dù đó có phải là vấn đề hay không phụ thuộc vào lớp bạn đang áp dụng nó cho (và phần còn lại của chương trình).
Ben

148

Trong hầu hết các ngôn ngữ OO nổi tiếng, một biểu thức như SomeClass(arg1, arg2)sẽ phân bổ một thể hiện mới, khởi tạo các thuộc tính của thể hiện và sau đó trả về nó.

Trong hầu hết các ngôn ngữ OO nổi tiếng, phần "khởi tạo thuộc tính của thể hiện" có thể được tùy chỉnh cho mỗi lớp bằng cách định nghĩa một hàm tạo , về cơ bản chỉ là một khối mã hoạt động trên thể hiện mới (sử dụng các đối số được cung cấp cho biểu thức hàm tạo ) để thiết lập bất kỳ điều kiện ban đầu nào được mong muốn. Trong Python, điều này tương ứng với __init__phương thức của lớp .

Python __new__là không có gì hơn và không có gì khác hơn là tùy chỉnh tương tự cho mỗi lớp của phần "phân bổ một thể hiện mới". Điều này tất nhiên cho phép bạn làm những việc khác thường như trả về một thể hiện hiện tại thay vì phân bổ một cái mới. Vì vậy, trong Python, chúng ta không thực sự nghĩ phần này là nhất thiết liên quan đến phân bổ; tất cả những gì chúng tôi yêu cầu là __new__đưa ra một ví dụ phù hợp từ đâu đó.

Nhưng đó vẫn chỉ là một nửa công việc và không có cách nào để hệ thống Python biết rằng đôi khi bạn muốn điều hành nửa công việc còn lại ( __init__) sau đó và đôi khi bạn không làm. Nếu bạn muốn hành vi đó, bạn phải nói một cách rõ ràng.

Thông thường, bạn có thể cấu trúc lại để bạn chỉ cần __new__, hoặc vì vậy bạn không cần __new__, hoặc do đó, __init__hành vi khác nhau trên một đối tượng đã được khởi tạo. Nhưng nếu bạn thực sự muốn, Python thực sự cho phép bạn xác định lại "công việc", do đó SomeClass(arg1, arg2)không nhất thiết phải gọi __new__theo sau __init__. Để làm điều này, bạn cần tạo một siêu dữ liệu và xác định __call__phương thức của nó .

Một metaclass chỉ là lớp của một lớp. Và __call__phương thức của lớp kiểm soát những gì xảy ra khi bạn gọi các thể hiện của lớp. Vì vậy, phương thức của siêu dữ liệu__call__ kiểm soát những gì xảy ra khi bạn gọi một lớp; tức là nó cho phép bạn xác định lại cơ chế tạo cá thể từ đầu đến cuối . Đây là mức độ mà bạn có thể thực hiện một cách thanh lịch nhất một quy trình tạo cá thể hoàn toàn không chuẩn như mẫu đơn. Trong thực tế, có ít hơn 10 dòng mã bạn có thể thực hiện một Singletonmetaclass mà sau đó thậm chí không yêu cầu bạn phải futz với __new__ ở tất cả , và có thể biến bất kỳ lớp khác bình thường thành một singleton bằng cách đơn giản thêm __metaclass__ = Singleton!

class Singleton(type):
    def __init__(self, *args, **kwargs):
        super(Singleton, self).__init__(*args, **kwargs)
        self.__instance = None
    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
        return self.__instance

Tuy nhiên, đây có lẽ là phép thuật sâu sắc hơn là thực sự được bảo đảm cho tình huống này!


25

Để trích dẫn tài liệu :

Các triển khai điển hình tạo ra một thể hiện mới của lớp bằng cách gọi phương thức __new __ () của siêu lớp bằng cách sử dụng "super (current class, cls) .__ new __ (cls [, ...])" với các đối số thích hợp và sau đó sửa đổi đối tượng mới được tạo nếu cần trước khi trả lại

...

Nếu __new __ () không trả về một thể hiện của cls, thì phương thức __init __ () của thể hiện mới sẽ không được gọi.

__new __ () chủ yếu nhằm cho phép các lớp con của các loại không thay đổi (như int, str hoặc tuple) để tùy chỉnh việc tạo cá thể.


18
If __new__() does not return an instance of cls, then the new instance's __init__() method will not be invoked.Đó là một điểm quan trọng. Nếu bạn trả về một thể hiện khác, bản gốc __init__không bao giờ được gọi.
Jeffrey Jose

@tgray Tôi nhận ra câu trả lời của bạn là từ các tài liệu, nhưng tôi tò mò nếu bạn biết bất kỳ trường hợp sử dụng nào không trả về một thể hiện của cls. Có vẻ như khi đối tượng trả về của lớp __new__được kiểm tra cho lớp của nó, phương thức sẽ đưa ra một lỗi thay vì cho phép kiểm tra thất bại trôi qua một cách im lặng, vì tôi không hiểu tại sao bạn lại muốn trả về bất cứ thứ gì ngoài một đối tượng của lớp .
soporific312

@ soporific312 Tôi chưa thấy trường hợp sử dụng nào. Câu trả lời này thảo luận về một số lý do cho thiết kế, mặc dù họ cũng chưa thấy bất kỳ mã nào tận dụng "tính năng" đó.
tgray

12

Tôi nhận ra rằng câu hỏi này khá cũ nhưng tôi đã có một vấn đề tương tự. Sau đây đã làm những gì tôi muốn:

class Agent(object):
    _agents = dict()

    def __new__(cls, *p):
        number = p[0]
        if not number in cls._agents:
            cls._agents[number] = object.__new__(cls)
        return cls._agents[number]

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

    def __eq__(self, rhs):
        return self.number == rhs.number

Agent("a") is Agent("a") == True

Tôi đã sử dụng trang này làm tài nguyên http://infohost.nmt.edu/tcc/help/pub/python/web/new-new-method.html


7
Lưu ý: __new__ luôn trả về một đối tượng thích hợp, vì vậy __init__ luôn được gọi - ngay cả khi thể hiện đã tồn tại.
Oddthinking

10

Tôi nghĩ rằng câu trả lời đơn giản cho câu hỏi này là, nếu __new__trả về một giá trị cùng loại với lớp, __init__hàm sẽ thực thi, nếu không thì sẽ không. Trong trường hợp này, mã của bạn trả về A._dict('key')cùng một lớp với cls, do đó __init__sẽ được thực thi.


10

Khi __new__trả về thể hiện của cùng một lớp, __init__được chạy sau đó trên đối tượng được trả về. Tức là bạn KHÔNG thể sử dụng __new__để ngăn __init__không cho chạy. Ngay cả khi bạn trả về đối tượng được tạo trước đó __new__, nó sẽ được nhân đôi (gấp ba, v.v.) được khởi tạo __init__lặp đi lặp lại.

Đây là cách tiếp cận chung cho mẫu Singleton mở rộng câu trả lời vartec ở trên và sửa nó:

def SingletonClass(cls):
    class Single(cls):
        __doc__ = cls.__doc__
        _initialized = False
        _instance = None

        def __new__(cls, *args, **kwargs):
            if not cls._instance:
                cls._instance = super(Single, cls).__new__(cls, *args, **kwargs)
            return cls._instance

        def __init__(self, *args, **kwargs):
            if self._initialized:
                return
            super(Single, self).__init__(*args, **kwargs)
            self.__class__._initialized = True  # Its crucial to set this variable on the class!
    return Single

Câu chuyện đầy đủ là đây .

Một cách tiếp cận khác, trong thực tế bao gồm __new__là sử dụng phân loại:

class Singleton(object):
    __initialized = False

    def __new__(cls, *args, **kwargs):
        if not cls.__initialized:
            cls.__init__(*args, **kwargs)
            cls.__initialized = True
        return cls


class MyClass(Singleton):
    @classmethod
    def __init__(cls, x, y):
        print "init is here"

    @classmethod
    def do(cls):
        print "doing stuff"

Xin lưu ý rằng với phương pháp này, bạn cần trang trí TẤT CẢ các phương thức của mình @classmethod, bởi vì bạn sẽ không bao giờ sử dụng bất kỳ trường hợp thực tế nào MyClass.


6

Nhắc đến tài liệu này :

Khi phân lớp các kiểu dựng sẵn bất biến như số và chuỗi, và đôi khi trong các tình huống khác, phương thức tĩnh mới có ích. mới là bước đầu tiên trong xây dựng thể hiện, được gọi trước init .

Các mới phương pháp được gọi với lớp như là đối số đầu tiên của nó; trách nhiệm của nó là trả về một thể hiện mới của lớp đó.

So sánh điều này với init : init được gọi với một thể hiện là đối số đầu tiên của nó và nó không trả về bất cứ điều gì; trách nhiệm của nó là khởi tạo thể hiện.

Có những tình huống trong đó một cá thể mới được tạo mà không gọi init (ví dụ khi cá thể được tải từ một dưa chua). Không có cách nào để tạo một cá thể mới mà không gọi mới (mặc dù trong một số trường hợp bạn có thể thoát khỏi việc gọi mới của lớp cơ sở ).

Về những gì bạn muốn đạt được, cũng có thông tin tài liệu tương tự về mẫu Singleton

class Singleton(object):
        def __new__(cls, *args, **kwds):
            it = cls.__dict__.get("__it__")
            if it is not None:
                return it
            cls.__it__ = it = object.__new__(cls)
            it.init(*args, **kwds)
            return it
        def init(self, *args, **kwds):
            pass

bạn cũng có thể sử dụng triển khai này từ PEP 318, sử dụng trang trí

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
...

1
Gọi một hình thức bastardized inittừ __new__âm thanh thực sự hacky. Đây là những gì metaclass dành cho.
Nhà vật lý điên

6
class M(type):
    _dict = {}

    def __call__(cls, key):
        if key in cls._dict:
            print 'EXISTS'
            return cls._dict[key]
        else:
            print 'NEW'
            instance = super(M, cls).__call__(key)
            cls._dict[key] = instance
            return instance

class A(object):
    __metaclass__ = M

    def __init__(self, key):
        print 'INIT'
        self.key = key
        print

a1 = A('aaa')
a2 = A('bbb')
a3 = A('aaa')

đầu ra:

NEW
INIT

NEW
INIT

EXISTS

NB Như một tác dụng phụ M._dictbất động sản tự động trở thành thể truy cập từ Anhư A._dictrất cẩn thận không ghi đè lên nó tình cờ.


Bạn đang thiếu một __init__phương thức thiết lập cls._dict = {}. Bạn có thể không muốn một từ điển được chia sẻ bởi tất cả các lớp của siêu dữ liệu này (nhưng +1 cho ý tưởng).
Nhà vật lý điên

5

__new__ sẽ trả về một thể hiện mới, trống của một lớp. __init__ sau đó được gọi để khởi tạo thể hiện đó. Bạn không gọi __init__ trong trường hợp "MỚI" của __new__, vì vậy nó được gọi cho bạn. Mã đang gọi __new__không theo dõi xem __init__ có được gọi trong một trường hợp cụ thể hay không cũng không nên bởi vì bạn đang làm điều gì đó rất bất thường ở đây.

Bạn có thể thêm một thuộc tính cho đối tượng trong hàm __init__ để chỉ ra rằng nó đã được khởi tạo. Kiểm tra sự tồn tại của thuộc tính đó như là điều đầu tiên trong __init__ và không tiến hành thêm nữa nếu nó đã xảy ra.


5

Một bản cập nhật cho câu trả lời @AntonyHatchkins, bạn có thể muốn có một từ điển các trường hợp riêng cho từng lớp của siêu dữ liệu, nghĩa là bạn nên có một __init__phương thức trong siêu dữ liệu để khởi tạo đối tượng lớp của bạn với từ điển đó thay vì làm cho nó toàn cầu trong tất cả các lớp.

class MetaQuasiSingleton(type):
    def __init__(cls, name, bases, attibutes):
        cls._dict = {}

    def __call__(cls, key):
        if key in cls._dict:
            print('EXISTS')
            instance = cls._dict[key]
        else:
            print('NEW')
            instance = super().__call__(key)
            cls._dict[key] = instance
        return instance

class A(metaclass=MetaQuasiSingleton):
    def __init__(self, key):
        print 'INIT'
        self.key = key
        print()

Tôi đã tiếp tục và cập nhật mã gốc bằng một __init__phương thức và thay đổi cú pháp thành ký hiệu Python 3 (cuộc gọi no-arg thànhsuper và metaclass trong các đối số lớp thay vì như một thuộc tính).

Dù bằng cách nào, điểm quan trọng ở đây là trình khởi tạo lớp ( __call__phương thức) của bạn sẽ không thực thi __new__hoặc __init__nếu tìm thấy khóa. Điều này sạch hơn nhiều so với việc sử dụng __new__, đòi hỏi bạn phải đánh dấu đối tượng nếu bạn muốn bỏ qua __init__bước mặc định .


4

Đào sâu hơn một chút vào đó!

Loại của một lớp chung trong CPython là typevà lớp cơ sở của nó là Object(Trừ khi bạn xác định rõ ràng một lớp cơ sở khác như siêu dữ liệu). Trình tự các cuộc gọi cấp thấp có thể được tìm thấy ở đây . Phương thức đầu tiên được gọi là phương thức type_callsau đó gọi tp_newvà sau đótp_init .

Phần thú vị ở đây là tp_newsẽ gọi Objectphương thức mới (lớp cơ sở) object_newthực hiện một tp_alloc( PyType_GenericAlloc) phân bổ bộ nhớ cho đối tượng :)

Tại thời điểm đó, đối tượng được tạo trong bộ nhớ và sau đó __init__phương thức được gọi. Nếu __init__không được thực hiện trong lớp của bạn thì object_initđược gọi và nó không làm gì cả :)

Sau đó, type_callchỉ cần trả về đối tượng liên kết với biến của bạn.


4

Chúng ta nên xem __init__như là một nhà xây dựng đơn giản trong các ngôn ngữ OO truyền thống. Ví dụ, nếu bạn quen thuộc với Java hoặc C ++, hàm tạo được truyền một con trỏ đến cá thể của chính nó. Trong trường hợp của Java, nó là thisbiến. Nếu một người kiểm tra mã byte được tạo cho Java, người ta sẽ nhận thấy hai cuộc gọi. Cuộc gọi đầu tiên là một phương thức "mới", và sau đó cuộc gọi tiếp theo là phương thức init (đây là cuộc gọi thực tế đến hàm tạo do người dùng định nghĩa). Quá trình hai bước này cho phép tạo ra cá thể thực tế trước khi gọi phương thức constructor của lớp, đây chỉ là một phương thức khác của cá thể đó.

Bây giờ, trong trường hợp của Python, __new__là một tiện ích bổ sung mà người dùng có thể truy cập được. Java không cung cấp tính linh hoạt đó, do bản chất được gõ của nó. Nếu một ngôn ngữ cung cấp cơ sở đó, thì người triển khai __new__có thể thực hiện nhiều điều trong phương thức đó trước khi trả về thể hiện, bao gồm cả việc tạo một thể hiện hoàn toàn mới của một đối tượng không liên quan trong một số trường hợp. Và, cách tiếp cận này cũng hoạt động tốt đặc biệt là đối với các loại không thay đổi trong trường hợp Python.


4

Tuy nhiên, tôi hơi bối rối về lý do tại sao __init__luôn được gọi sau __new__.

Tôi nghĩ rằng sự tương tự C ++ sẽ hữu ích ở đây:

  1. __new__chỉ đơn giản là phân bổ bộ nhớ cho đối tượng. Các biến đối tượng của một đối tượng cần bộ nhớ để giữ nó và đây là những gì bước __new__sẽ làm.

  2. __init__ khởi tạo các biến nội bộ của đối tượng thành các giá trị cụ thể (có thể được mặc định).


2

Cái __init__được gọi là sau__new__ để khi bạn ghi đè nó trong một lớp con, mã được thêm của bạn sẽ vẫn được gọi.

Nếu bạn đang cố gắng phân lớp một lớp đã có __new__, một người không biết điều này có thể bắt đầu bằng cách điều chỉnh __init__và chuyển cuộc gọi xuống lớp con __init__. Quy ước gọi __init__sau__new__ này giúp công việc như mong đợi.

Các __init__vẫn cần để cho phép bất kỳ thông số của lớp cha __new__cần thiết, nhưng không phải làm như vậy thường sẽ tạo ra một lỗi thời gian chạy rõ ràng. Và __new__có lẽ nên cho phép rõ ràng *argsvà '** kw', để làm rõ rằng tiện ích mở rộng là OK.

Nhìn chung, hình thức xấu là có cả hai __new____init__trong cùng một lớp ở cùng một mức độ kế thừa, vì hành vi mà người đăng ban đầu mô tả.


1

Tuy nhiên, tôi hơi bối rối về lý do tại sao __init__luôn được gọi sau __new__.

Không có nhiều lý do khác hơn là nó được thực hiện theo cách đó. __new__không có trách nhiệm khởi tạo lớp, một số phương pháp khác thực hiện ( __call__có thể-- tôi không biết chắc).

Tôi đã không mong đợi điều này. Bất cứ ai có thể cho tôi biết tại sao điều này xảy ra và làm thế nào tôi thực hiện chức năng này? (ngoài việc đưa việc thực hiện vào __new__đó mà cảm thấy khá khó khăn).

Bạn có thể __init__không làm gì nếu nó đã được khởi tạo hoặc bạn có thể viết một siêu dữ liệu mới với một cái mới __call__chỉ gọi __init__trong các trường hợp mới, và nếu không thì chỉ trả về __new__(...).


1

Lý do đơn giản là cái mới được sử dụng để tạo một thể hiện, trong khi init được sử dụng để khởi tạo thể hiện. Trước khi khởi tạo, thể hiện nên được tạo trước. Đó là lý do tại sao mới nên được gọi trước khi init .


1

Bây giờ tôi đã có cùng một vấn đề, và vì một số lý do, tôi quyết định tránh trang trí, nhà máy và siêu dữ liệu. Tôi đã làm nó như thế này:

Tập tin chính

def _alt(func):
    import functools
    @functools.wraps(func)
    def init(self, *p, **k):
        if hasattr(self, "parent_initialized"):
            return
        else:
            self.parent_initialized = True
            func(self, *p, **k)

    return init


class Parent:
    # Empty dictionary, shouldn't ever be filled with anything else
    parent_cache = {}

    def __new__(cls, n, *args, **kwargs):

        # Checks if object with this ID (n) has been created
        if n in cls.parent_cache:

            # It was, return it
            return cls.parent_cache[n]

        else:

            # Check if it was modified by this function
            if not hasattr(cls, "parent_modified"):
                # Add the attribute
                cls.parent_modified = True
                cls.parent_cache = {}

                # Apply it
                cls.__init__ = _alt(cls.__init__)

            # Get the instance
            obj = super().__new__(cls)

            # Push it to cache
            cls.parent_cache[n] = obj

            # Return it
            return obj

Các lớp mẫu

class A(Parent):

    def __init__(self, n):
        print("A.__init__", n)


class B(Parent):

    def __init__(self, n):
        print("B.__init__", n)

Đang sử dụng

>>> A(1)
A.__init__ 1  # First A(1) initialized 
<__main__.A object at 0x000001A73A4A2E48>
>>> A(1)      # Returned previous A(1)
<__main__.A object at 0x000001A73A4A2E48>
>>> A(2)
A.__init__ 2  # First A(2) initialized
<__main__.A object at 0x000001A7395D9C88>
>>> B(2)
B.__init__ 2  # B class doesn't collide with A, thanks to separate cache
<__main__.B object at 0x000001A73951B080>
  • Cảnh báo: Bạn không nên khởi tạo Parent, nó sẽ va chạm với các lớp khác - trừ khi bạn xác định bộ đệm riêng biệt ở mỗi đứa trẻ, đó không phải là điều chúng ta muốn.
  • Cảnh báo: Có vẻ như một lớp học với Cha mẹ là ông bà cư xử kỳ lạ. [Chưa được xác minh]

Hãy thử trực tuyến!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.