programing

기존 개체 인스턴스에 메서드 추가

nasanasas 2020. 10. 3. 10:54
반응형

기존 개체 인스턴스에 메서드 추가


파이썬에서 기존 객체 (즉, 클래스 정의가 아닌)에 메서드를 추가 할 수 있다는 것을 읽었습니다.

그렇게하는 것이 항상 좋은 것은 아니라는 것을 이해합니다. 그러나 어떻게 이것을 할 수 있습니까?


Python에서는 함수와 바인딩 된 메서드 사이에 차이가 있습니다.

>>> def foo():
...     print "foo"
...
>>> class A:
...     def bar( self ):
...         print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>

바인딩 된 메서드는 인스턴스에 "바인드"되어 있으며 (설명 적) 메서드가 호출 될 때마다 해당 인스턴스가 첫 번째 인수로 전달됩니다.

클래스의 속성 인 콜 러블 (인스턴스와 반대)은 여전히 ​​바인딩되지 않으므로 원할 때마다 클래스 정의를 수정할 수 있습니다.

>>> def fooFighters( self ):
...     print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters

이전에 정의 된 인스턴스도 업데이트됩니다 (속성 자체를 재정의하지 않은 경우).

>>> a.fooFighters()
fooFighters

문제는 단일 인스턴스에 메서드를 연결하려는 경우 발생합니다.

>>> def barFighters( self ):
...     print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)

이 함수는 인스턴스에 직접 연결될 때 자동으로 바인딩되지 않습니다.

>>> a.barFighters
<function barFighters at 0x00A98EF0>

바인딩하려면 types 모듈에서 MethodType 함수를 사용할 수 있습니다 .

>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

이번에는 클래스의 다른 인스턴스가 영향을받지 않았습니다.

>>> a2.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'

더 많은 정보는 디스크립터메타 클래스 프로그래밍 을 읽어 보면 찾을 수 있습니다 .


새로운 모듈 은 파이썬 2.6 이후로 사용되지 않으며 3.0에서 제거되었습니다. 유형을 사용하십시오.

참조 http://docs.python.org/library/new.html를

아래 예제에서는 의도적으로 patch_me()함수 에서 반환 값을 제거했습니다 . 반환 값을 제공하면 패치가 참이 아닌 새 객체를 반환한다고 믿을 수 있다고 생각합니다. 이는 들어오는 객체를 수정합니다. 아마도 이것은 몽키 패칭의보다 체계적인 사용을 용이하게 할 수 있습니다.

import types

class A(object):#but seems to work for old style objects too
    pass

def patch_me(target):
    def method(target,x):
        print "x=",x
        print "called from", target
    target.method = types.MethodType(method,target)
    #add more if needed

a = A()
print a
#out: <__main__.A object at 0x2b73ac88bfd0>  
patch_me(a)    #patch instance
a.method(5)
#out: x= 5
#out: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)
A.method(6)        #can patch class too
#out: x= 6
#out: called from <class '__main__.A'>

서문-호환성에 대한 참고 사항 : 다른 답변은 Python 2에서만 작동 할 수 있습니다.이 답변은 Python 2 및 3에서 완벽하게 작동합니다. Python 3 만 작성하는 경우 명시 적으로 상속을 생략 할 수 object있지만 그렇지 않으면 코드는 동일하게 유지되어야합니다. .

기존 개체 인스턴스에 메서드 추가

파이썬에서 기존 객체 (예 : 클래스 정의가 아님)에 메서드를 추가 할 수 있다는 것을 읽었습니다.

그렇게하는 것이 항상 좋은 결정은 아니라는 것을 이해합니다. 그러나 어떻게 이것을 할 수 있습니까?

예, 가능합니다.하지만 권장하지 않습니다.

나는 이것을 권장하지 않습니다. 이것은 나쁜 생각입니다. 하지마.

몇 가지 이유가 있습니다.

  • 이 작업을 수행하는 모든 인스턴스에 바인딩 된 개체를 추가합니다. 이 작업을 많이 수행하면 아마도 많은 메모리를 낭비하게 될 것입니다. 바인딩 된 메서드는 일반적으로 호출의 짧은 기간 동안 만 생성되며 자동으로 가비지 수집되면 존재하지 않습니다. 이 작업을 수동으로 수행하면 바인딩 된 메서드를 참조하는 이름 바인딩이 있으므로 사용시 가비지 수집이 방지됩니다.
  • 주어진 유형의 개체 인스턴스는 일반적으로 해당 유형의 모든 개체에 대한 메서드를 갖습니다. 다른 곳에 메서드를 추가하면 일부 인스턴스에는 해당 메서드가 있고 다른 인스턴스에는 없습니다. 프로그래머는 이것을 기대하지 않을 것이며, 최소한 놀라움규칙을 위반할 위험이 있습니다 .
  • 이렇게하지 말아야 할 다른 정말 좋은 이유가 있기 때문에 그렇게하면 자신에게 더 나쁜 평판을 줄 수 있습니다.

따라서 정말 좋은 이유가 없으면 이렇게하지 않는 것이 좋습니다. 클래스 정의에서 올바른 메서드를 정의하는 것이 훨씬 낫 거나 바람직하게는 다음과 같이 클래스를 직접 몽키 패치하는 것이 좋습니다.

Foo.sample_method = sample_method

그러나 그것은 유익하기 때문에 이것을하는 몇 가지 방법을 보여줄 것입니다.

수행 방법

다음은 몇 가지 설정 코드입니다. 클래스 정의가 필요합니다. 가져올 수는 있지만 실제로는 중요하지 않습니다.

class Foo(object):
    '''An empty class to demonstrate adding a method to an instance'''

인스턴스를 만듭니다.

foo = Foo()

추가 할 메서드를 만듭니다.

def sample_method(self, bar, baz):
    print(bar + baz)

메서드 없음 (0)-설명자 메서드 사용, __get__

함수에 대한 점선 조회 __get__는 인스턴스와 함께 함수 메서드를 호출하여 개체를 메서드에 바인딩하여 "바운드 메서드"를 만듭니다.

foo.sample_method = sample_method.__get__(foo)

그리고 지금:

>>> foo.sample_method(1,2)
3

방법 1-types.MethodType

먼저, 메소드 생성자를 가져올 유형을 가져옵니다.

import types

이제 인스턴스에 메서드를 추가합니다. 이를 위해 types모듈 (위에서 가져온) 의 MethodType 생성자가 필요합니다 .

types.MethodType에 대한 인수 서명은 (function, instance, class)다음과 같습니다.

foo.sample_method = types.MethodType(sample_method, foo, Foo)

및 사용법 :

>>> foo.sample_method(1,2)
3

방법 2 : 어휘 바인딩

먼저 메서드를 인스턴스에 바인딩하는 래퍼 함수를 ​​만듭니다.

def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn

용법:

>>> foo.sample_method = bind(foo, sample_method)    
>>> foo.sample_method(1,2)
3

방법 3 : functools.partial

부분 함수는 첫 번째 인수를 함수 (및 선택적으로 키워드 인수)에 적용하고 나중에 나머지 인수 (및 키워드 인수 재정의)로 호출 할 수 있습니다. 그러므로:

>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3    

이는 바인딩 된 메서드가 인스턴스의 부분 함수라고 생각할 때 의미가 있습니다.

객체 속성으로서의 언 바운드 기능-이것이 작동하지 않는 이유 :

sample_method를 클래스에 추가하는 것과 같은 방식으로 추가하려고하면 인스턴스에서 바인딩 해제되고 암시 적 self를 첫 번째 인수로 사용하지 않습니다.

>>> foo.sample_method = sample_method
>>> foo.sample_method(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sample_method() takes exactly 3 arguments (2 given)

인스턴스 (또는이 메서드는 실제로 self인수 변수를 사용하지 않기 때문에)를 명시 적으로 전달하여 바인딩 해제 된 함수가 작동하도록 만들 수 있지만, 다른 인스턴스의 예상 서명과 일치하지 않습니다 (원숭이 패치를 적용하는 경우). 이 인스턴스) :

>>> foo.sample_method(foo, 1, 2)
3

결론

이제 당신 은 이것을 있는 몇 가지 방법을 알고 있지만, 진지하게-이것을하지 마십시오.


위의 답변은 요점을 놓친 것 같습니다.

메서드가있는 클래스를 만들어 보겠습니다.

class A(object):
    def m(self):
        pass

이제 ipython에서 놀아 보겠습니다.

In [2]: A.m
Out[2]: <unbound method A.m>

그래서 m ()은 어떻게 든 A 의 언 바운드 메서드가 됩니다. 하지만 정말 그런가요?

In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>

m ()A 클래스 사전에 추가되는 참조 인 함수일 뿐이라는 것이 밝혀졌습니다 . 마법은 없습니다. 그렇다면 왜 Am 은 우리에게 언 바운드 방법을 제공합니까? 점이 간단한 사전 조회로 번역되지 않았기 때문입니다. 사실상 A .__ class __.__ getattribute __ (A, 'm')의 호출입니다.

In [11]: class MetaA(type):
   ....:     def __getattribute__(self, attr_name):
   ....:         print str(self), '-', attr_name

In [12]: class A(object):
   ....:     __metaclass__ = MetaA

In [23]: A.m
<class '__main__.A'> - m
<class '__main__.A'> - m

이제 마지막 줄이 두 번 인쇄되는 이유가 머릿속에서 확실하지 않지만 여전히 거기에서 무슨 일이 일어나고 있는지 분명합니다.

이제 기본 __getattribute__이하는 일은 속성이 소위 설명자 인지 아닌지, 즉 특수한 __get__ 메소드를 구현하는지 확인하는 것입니다. 해당 메서드를 구현하면 반환되는 것은 해당 __get__ 메서드를 호출 한 결과입니다. A 클래스 의 첫 번째 버전으로 돌아 가면 다음과 같습니다.

In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>

그리고 파이썬 함수는 설명자 프로토콜을 구현하기 때문에 객체 대신 호출되면 __get__ 메서드에서 해당 객체에 자신을 바인딩합니다.

좋아, 기존 객체에 메소드를 추가하는 방법은 무엇입니까? 클래스 패치에 신경 쓰지 않는다고 가정하면 다음과 같이 간단합니다.

B.m = m

그런 다음 Bm 은 설명자 마법 덕분에 언 바운드 메서드가됩니다.

단일 객체에만 메서드를 추가하려면 types.MethodType을 사용하여 직접 기계를 에뮬레이션해야합니다.

b.m = types.MethodType(m, b)

그건 그렇고 :

In [2]: A.m
Out[2]: <unbound method A.m>

In [59]: type(A.m)
Out[59]: <type 'instancemethod'>

In [60]: type(b.m)
Out[60]: <type 'instancemethod'>

In [61]: types.MethodType
Out[61]: <type 'instancemethod'>

Python에서 원숭이 패치는 일반적으로 클래스 또는 함수 서명을 자신의 것으로 덮어 쓰는 방식으로 작동합니다. 아래는 Zope Wiki 의 예입니다 .

from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
   return "ook ook eee eee eee!"
SomeClass.speak = speak

이 코드는 클래스에서 speak라는 메서드를 덮어 쓰거나 만듭니다. 원숭이 패치에 대한 Jeff Atwood의 최근 게시물에서 . 그는 현재 제가 업무에 사용하는 언어 인 C # 3.0의 예를 보여줍니다.


람다를 사용하여 메서드를 인스턴스에 바인딩 할 수 있습니다.

def run(self):
    print self._instanceString

class A(object):
    def __init__(self):
        self._instanceString = "This is instance string"

a = A()
a.run = lambda: run(a)
a.run()

산출:

This is instance string

다음없이 인스턴스에 메서드를 연결하는 방법에는 두 가지 이상이 있습니다 types.MethodType.

>>> class A:
...  def m(self):
...   print 'im m, invoked with: ', self

>>> a = A()
>>> a.m()
im m, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.m
<bound method A.m of <__main__.A instance at 0x973ec6c>>
>>> 
>>> def foo(firstargument):
...  print 'im foo, invoked with: ', firstargument

>>> foo
<function foo at 0x978548c>

1:

>>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a))
>>> a.foo()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo
<bound method A.foo of <__main__.A instance at 0x973ec6c>>

2 :

>>> instancemethod = type(A.m)
>>> instancemethod
<type 'instancemethod'>
>>> a.foo2 = instancemethod(foo, a, type(a))
>>> a.foo2()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo2
<bound method instance.foo of <__main__.A instance at 0x973ec6c>>

유용한 링크 :
데이터 모델-설명자 호출 설명자
하우투 가이드-설명자 호출


당신이 찾고있는 것은 setattr내가 믿는 것입니다. 이를 사용하여 개체에 대한 속성을 설정합니다.

>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>

이 질문은 Python이 아닌 버전을 요청했기 때문에 JavaScript는 다음과 같습니다.

a.methodname = function () { console.log("Yay, a new method!") }

다양한 바인딩 방법의 결과를 살펴보면서 Jason Pratt와 커뮤니티 위키 답변을 통합합니다.

특히 바인딩 함수를 클래스 메서드로 추가하는 방법이 작동 하지만 참조 범위가 잘못되었습니다.

#!/usr/bin/python -u
import types
import inspect

## dynamically adding methods to a unique instance of a class


# get a list of a class's method type attributes
def listattr(c):
    for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v,types.MethodType)]:
        print m[0], m[1]

# externally bind a function as a method of an instance of a class
def ADDMETHOD(c, method, name):
    c.__dict__[name] = types.MethodType(method, c)

class C():
    r = 10 # class attribute variable to test bound scope

    def __init__(self):
        pass

    #internally bind a function as a method of self's class -- note that this one has issues!
    def addmethod(self, method, name):
        self.__dict__[name] = types.MethodType( method, self.__class__ )

    # predfined function to compare with
    def f0(self, x):
        print 'f0\tx = %d\tr = %d' % ( x, self.r)

a = C() # created before modified instnace
b = C() # modified instnace


def f1(self, x): # bind internally
    print 'f1\tx = %d\tr = %d' % ( x, self.r )
def f2( self, x): # add to class instance's .__dict__ as method type
    print 'f2\tx = %d\tr = %d' % ( x, self.r )
def f3( self, x): # assign to class as method type
    print 'f3\tx = %d\tr = %d' % ( x, self.r )
def f4( self, x): # add to class instance's .__dict__ using a general function
    print 'f4\tx = %d\tr = %d' % ( x, self.r )


b.addmethod(f1, 'f1')
b.__dict__['f2'] = types.MethodType( f2, b)
b.f3 = types.MethodType( f3, b)
ADDMETHOD(b, f4, 'f4')


b.f0(0) # OUT: f0   x = 0   r = 10
b.f1(1) # OUT: f1   x = 1   r = 10
b.f2(2) # OUT: f2   x = 2   r = 10
b.f3(3) # OUT: f3   x = 3   r = 10
b.f4(4) # OUT: f4   x = 4   r = 10


k = 2
print 'changing b.r from {0} to {1}'.format(b.r, k)
b.r = k
print 'new b.r = {0}'.format(b.r)

b.f0(0) # OUT: f0   x = 0   r = 2
b.f1(1) # OUT: f1   x = 1   r = 10  !!!!!!!!!
b.f2(2) # OUT: f2   x = 2   r = 2
b.f3(3) # OUT: f3   x = 3   r = 2
b.f4(4) # OUT: f4   x = 4   r = 2

c = C() # created after modifying instance

# let's have a look at each instance's method type attributes
print '\nattributes of a:'
listattr(a)
# OUT:
# attributes of a:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>>

print '\nattributes of b:'
listattr(b)
# OUT:
# attributes of b:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>>
# f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>>
# f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>>
# f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>>
# f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>>

print '\nattributes of c:'
listattr(c)
# OUT:
# attributes of c:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>>

개인적으로 외부 ADDMETHOD 함수 경로를 선호합니다. 반복자 내에서도 새 메서드 이름을 동적으로 할당 할 수 있기 때문입니다.

def y(self, x):
    pass
d = C()
for i in range(1,5):
    ADDMETHOD(d, y, 'f%d' % i)
print '\nattributes of d:'
listattr(d)
# OUT:
# attributes of d:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>>
# f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>

이것은 실제로 "Jason Pratt"의 대답에 대한 추가 기능입니다.

Jasons 대답은 작동하지만 클래스에 함수를 추가하려는 경우에만 작동합니다. .py 소스 코드 파일에서 이미 존재하는 메서드를 다시로드하려고 할 때 작동하지 않았습니다.

해결 방법을 찾는 데 오랜 시간이 걸렸지 만 트릭은 간단 해 보입니다 ... 1.st 소스 코드 파일에서 코드를 가져옵니다. 2.nd 강제로 다시로드 3.rd를 사용하여 types.FunctionType (...) 다시로드 된 메서드가 다른 네임 스페이스에 있기 때문에 현재 전역 변수를 전달할 수도 있습니다. 4. 이제 types.MethodType (... )

예:

# this class resides inside ReloadCodeDemo.py
class A:
    def bar( self ):
        print "bar1"

    def reloadCode(self, methodName):
        ''' use this function to reload any function of class A'''
        import types
        import ReloadCodeDemo as ReloadMod # import the code as module
        reload (ReloadMod) # force a reload of the module
        myM = getattr(ReloadMod.A,methodName) #get reloaded Method
        myTempFunc = types.FunctionType(# convert the method to a simple function
                                myM.im_func.func_code, #the methods code
                                globals(), # globals to use
                                argdefs=myM.im_func.func_defaults # default values for variables if any
                                ) 
        myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a method
        setattr(self,methodName,myNewM) # add the method to the function

if __name__ == '__main__':
    a = A()
    a.bar()
    # now change your code and save the file
    a.reloadCode('bar') # reloads the file
    a.bar() # now executes the reloaded code

도움이 될 수 있다면, 원숭이 패치 프로세스를보다 편리하게 만들기 위해 최근 Gorilla라는 Python 라이브러리를 출시했습니다.

함수 needle()사용하여 이름 guineapig지정된 모듈을 패치하는 방법 은 다음과 같습니다.

import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
    print("awesome")

같이 그러나 그것은 또한 더 흥미로운 사용 사례를 돌봐 자주 묻는 질문 로부터 문서 .

코드는 GitHub에서 사용할 수 있습니다 .


Jason Pratt가 게시 한 내용이 정확합니다.

>>> class Test(object):
...   def a(self):
...     pass
... 
>>> def b(self):
...   pass
... 
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>

보시다시피, 파이썬은 b ()를 a ()와 다른 것으로 간주하지 않습니다. 파이썬에서 모든 메소드는 함수가되는 변수 일뿐입니다.


이 질문은 몇 년 전에 열렸지만 데코레이터를 사용하여 클래스 인스턴스에 대한 함수 바인딩을 시뮬레이션하는 쉬운 방법이 있습니다.

def binder (function, instance):
  copy_of_function = type (function) (function.func_code, {})
  copy_of_function.__bind_to__ = instance
  def bound_function (*args, **kwargs):
    return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs)
  return bound_function


class SupaClass (object):
  def __init__ (self):
    self.supaAttribute = 42


def new_method (self):
  print self.supaAttribute


supaInstance = SupaClass ()
supaInstance.supMethod = binder (new_method, supaInstance)

otherInstance = SupaClass ()
otherInstance.supaAttribute = 72
otherInstance.supMethod = binder (new_method, otherInstance)

otherInstance.supMethod ()
supaInstance.supMethod ()

거기에서 함수와 인스턴스를 바인더 데코레이터에 전달하면 첫 번째 코드 객체와 동일한 코드 객체를 사용하여 새 함수가 생성됩니다. 그런 다음 클래스의 지정된 인스턴스가 새로 생성 된 함수의 속성에 저장됩니다. 데코레이터는 복사 된 함수를 자동으로 호출하는 (세 번째) 함수를 반환하여 인스턴스를 첫 번째 매개 변수로 제공합니다.

결론적으로 클래스 인스턴스에 바인딩하는 것을 시뮬레이션하는 함수를 얻습니다. 원래 기능을 변경하지 않습니다.


위에 나열된 모든 메서드가 추가 된 메서드와 인스턴스 사이에 순환 참조를 생성하여 가비지 수집까지 개체가 지속되도록한다고 언급 한 사람이 아무도 없다는 점이 이상합니다. 객체의 클래스를 확장하여 설명자를 추가하는 오래된 트릭이 있습니다.

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass

from types import MethodType

def method(self):
   print 'hi!'


setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )

이것으로 셀프 포인터를 사용할 수 있습니다

참고URL : https://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object-instance

반응형