Tilsette en metode til et eksisterende objekt forekomst

stemmer
512

Jeg har lest at det er mulig å legge en metode til et eksisterende objekt (dvs. ikke i klassen definisjon) i Python.

Jeg forstår at det er ikke alltid godt å gjøre det. Men hvordan kan man gjøre dette?

Publisert på 04/08/2008 klokken 02:17
kilden bruker
På andre språk...                            


18 svar

stemmer
765

I Python, er det en forskjell mellom funksjoner og bundet metoder.

>>> 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>>
>>>

Bundet metoder har blitt "bundet" (hvor beskrivende) til et eksempel, og at forekomsten skal sendes som den første argumentet når metoden kalles.

Callables som er egenskapene til en klasse (i motsetning til en forekomst) fortsatt er ubundet, skjønt, så du kan endre klassen definisjonen når du vil:

>>> 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

Tidligere definerte forekomster oppdateres også (så lenge de ikke har overstyrt attributtet selv):

>>> a.fooFighters()
fooFighters

Problemet kommer når du vil legge ved en metode til en enkelt forekomst:

>>> 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)

Funksjonen blir ikke automatisk bundet når den er koblet direkte til en forekomst:

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

Å binde den, kan vi bruke MethodType funksjon i typer modul :

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

Denne gangen andre forekomster av klassen har ikke blitt berørt:

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

Mer informasjon finner du ved å lese om beskrivelsene og metaclass programmering .

Svarte 06/08/2008 kl. 00:33
kilden bruker

stemmer
81

Modul nye er foreldet fordi python 2.6 og fjernet i 3,0, bruk typer

se http://docs.python.org/library/new.html

I eksempelet under har jeg bevisst fjernet returverdi fra patch_me()funksjonen. Jeg tror at det å gi returverdi kan gjøre en tro at patch returnerer et nytt objekt, som ikke er sant - det endrer det nye. Trolig kan dette legge til rette for en mer disiplinert bruk av monkeypatching.

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'>
Svarte 06/06/2009 kl. 05:31
kilden bruker

stemmer
53

Tilsette en metode til et eksisterende objekt forekomst

Jeg har lest at det er mulig å legge en metode til et eksisterende objekt (f.eks ikke i klassen definisjon) i Python.

Jeg forstår at det er ikke alltid en god beslutning om å gjøre det. Men hvordan kan man gjøre dette?

Ja, det er mulig - men ikke anbefalt

Jeg anbefaler ikke dette. Dette er en dårlig idé. Ikke gjør det.

Her er et par grunner:

  • Du vil legge et bundet objekt til hver forekomst du gjør dette til. Hvis du gjør dette mye, vil du sannsynligvis kaste bort en masse minne. Bundet metoder er vanligvis bare skapt for den korte varigheten av samtalen, og de deretter opphøre å eksistere når automatisk søppel samles. Hvis du gjør dette manuelt, vil du ha et navn bindende refererer bundet metode - som vil hindre søppelrydding på bruk.
  • Objekt forekomster av en gitt type har generelt sine metoder på alle objekter av denne typen. Hvis du legger til metoder andre steder, vil noen tilfeller har disse metodene og andre vil ikke. Programmerere vil ikke forvente dette, og du risikerer å bryte regelen om minst overraskelse .
  • Siden det er andre gode grunner til ikke å gjøre dette, vil du i tillegg gi deg selv et dårlig rykte hvis du gjør det.

Derfor foreslår jeg at du ikke gjør dette med mindre du har en veldig god grunn. Det er mye bedre å bestemme den riktige fremgangsmåten i den klassedefinisjon eller mindre , fortrinnsvis til ape-patch klassen direkte, slik som dette:

Foo.sample_method = sample_method

Siden det er lærerikt, men jeg skal vise deg noen måter å gjøre dette.

Hvordan det kan gjøres

Her er noen oppsett kode. Vi trenger en klassedefinisjon. Det kan bli importert, men det spiller egentlig ingen rolle.

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

Opprett en forekomst:

foo = Foo()

Lag en metode for å legge til det:

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

Metode null (0) - bruk deskriptor-metoden, __get__

Stiplede oppslag på funksjoner ring __get__metode for funksjonen med eksempel binding av gjenstanden til metoden og dermed skape en "grense-metoden".

foo.sample_method = sample_method.__get__(foo)

og nå:

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

Metode en - types.MethodType

Først importtyper, som vi får metoden konstruktør:

import types

Nå legger vi metoden til forekomsten. For å gjøre dette, trenger vi MethodType konstruktør fra typesmodulen (som vi importert ovenfor).

Argumentet signatur for types.MethodType er (function, instance, class):

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

og bruk:

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

Metode to: leksikalske bindings

Først må vil lage et omslag funksjon som binder metoden til forekomsten:

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

bruk:

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

Metode tre: functools.partial

En delvis funksjonen gjelder den første argumentet (e) til en funksjon (og eventuelt søkeord argumenter), og kan senere bli kalt med de resterende argumenter (og overordnede søkeord argumenter). Og dermed:

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

Dette er fornuftig når du tenker på at innbundne metoder er delvis funksjoner av forekomsten.

Ubundet funksjon som et objekt egenskap - hvorfor dette ikke fungerer:

Hvis vi prøver å legge til sample_method på samme måte som vi kan legge det til klassen, er det ubundet fra forekomsten, og tar ikke den implisitte selv som det første argumentet.

>>> 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)

Vi kan gjøre den ubundne funksjon arbeidet ved å eksplisitt passerer forekomsten (eller noe, siden denne metoden ikke faktisk bruke selfargumentet variable), men det ville ikke være i samsvar med den forventede signaturen til andre tilfeller (hvis vi er ape-lapp dette eksempel):

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

Konklusjon

Du vet nå flere måter du kan gjøre dette, men i all seriøsitet - ikke gjør dette.

Svarte 21/01/2015 kl. 05:31
kilden bruker

stemmer
31

Jeg tror at de ovennevnte svar savnet sentralt punkt.

La oss ta en klasse med en metode:

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

Nå, la oss leke med den i ipython:

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

OK, så m () eller annen måte blir en ubundet metode for A . Men er det virkelig slik?

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

Det viser seg at m () er bare en funksjon, referanse til noe som er lagt til en klasse ordbok - det er ingen magi. Så hvorfor Am gir oss en ubundet metode? Det er fordi prikken ikke er oversatt til en enkel ordbok oppslag. Det er de facto en samtale med A .__ klasse __.__ 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

Nå er jeg ikke sikker ut av toppen av hodet mitt hvorfor den siste linjen er skrevet ut to ganger, men fortsatt er det uklart hva som skjer der.

Nå, hva er standard __getattribute__ gjør er at den sjekker om attributtet er en såkalt descriptor eller ikke, dvs. hvis den implementerer en spesiell __get__ metode. Hvis det implementerer denne metoden, så hva er returnert er resultatet av å kalle det __get__ metoden. Går tilbake til den første versjonen av vår A klasse, dette er hva vi har:

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

Og fordi Python funksjoner implementere descriptor protokollen, hvis de blir kalt på vegne av et objekt, binder de seg til at objektet i sin __get__ metode.

Ok, så hvordan du legger til en metode til et eksisterende objekt? Forutsatt at du ikke har noe imot patching klasse, er det så enkelt som:

B.m = m

Deretter Bm "blir" en ubundet metode, takket være descriptor magi.

Og hvis du ønsker å legge til en metode bare et enkelt objekt, så må du etterligne maskiner selv, ved hjelp types.MethodType:

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

Forresten:

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'>
Svarte 22/01/2012 kl. 14:20
kilden bruker

stemmer
15

I Python ape patching generelt fungerer ved å overskrive en klasse eller funksjoner signatur med din egen. Nedenfor er et eksempel fra Zope Wiki :

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

At koden vil overskrive / lage en metode som kalles snakke om klasse. I Jeff Atwood er siste innlegg på ape patching . Han viser et eksempel i C # 3.0 som er det aktuelle språket jeg bruker på jobb.

Svarte 04/08/2008 kl. 02:31
kilden bruker

stemmer
9

Det er minst to måter for å feste en metode til en forekomst uten 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>>

Nyttige lenker:
datamodell - påkalle beskrivelsene
Descriptor HowTo Guide - påkalle beskrivelsene

Svarte 26/04/2013 kl. 15:47
kilden bruker

stemmer
7

Man kan bruke lambda å binde en metode til en forekomst:

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()

Dette er tilfellet strengen

Prosessen er ferdig med utgang kode 0

Svarte 21/07/2014 kl. 12:55
kilden bruker

stemmer
6

Siden dette spørsmålet spurte for ikke-Python-versjoner, her er Javascript:

a.methodname = function () { console.log("Yay, a new method!") }
Svarte 09/03/2012 kl. 15:07
kilden bruker

stemmer
6

Det du leter etter er setattrtror jeg. Bruk dette til å angi en attributt på et objekt.

>>> 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>
Svarte 07/08/2008 kl. 11:30
kilden bruker

stemmer
5

Dette er faktisk en addon til svaret på "Jason Pratt"

Selv om Jasons svare fungerer, betyr det bare fungere hvis man ønsker å legge til en funksjon til en klasse. Det fungerte ikke for meg da jeg prøvde å laste en allerede eksisterende metode fra .py kildekoden fil.

Det tok meg i evigheter for å finne en løsning, men kunsten virker enkelt ... 1.st importere koden fra kildekoden filen 2.nd tvinge en reload 3.rd bruk types.FunctionType (...) for å konvertere importert og bundet metode til en funksjon kan du også passere på dagens globale variabler, som på nytt metode vil være i en annen navne 4.th nå kan du fortsette som foreslått av "Jason Pratt" med types.MethodType (... )

Eksempel:

# 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
Svarte 18/08/2015 kl. 15:32
kilden bruker

stemmer
5

Dere bør virkelig se på forbuden frukt , er det en python bibliotek som gir støtte til ape patching NOEN python klasse, selv strenger.

Svarte 25/08/2013 kl. 21:56
kilden bruker

stemmer
5

Konsolidering Jason Pratt og samfunnet wiki svar, med en titt på resultatene av ulike metoder for binding:

Legg spesielt merke til hvordan tilsetning av binde funksjon som en klasse metode fungerer , men refererer omfang er feil.

#!/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>>

Personlig foretrekker jeg den eksterne ADDMETHOD funksjon rute, som det tillater meg å tildele dynamisk nye metodenavn innenfor en iterator også.

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>>
Svarte 28/01/2012 kl. 00:12
kilden bruker

stemmer
3

Hvis det kan være til hjelp, jeg har nylig gitt ut en Python bibliotek som heter Gorilla å gjøre prosessen med ape patching mer praktisk.

Ved hjelp av en funksjon needle()for å lappe en modul som heter guineapiggår som følger:

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

Men det tar også vare på mer interessante bruksmåter som vist i FAQ fra dokumentasjonen .

Koden er tilgjengelig på GitHub .

Svarte 15/07/2014 kl. 02:12
kilden bruker

stemmer
3

Hva Jason Pratt postet er riktig.

>>> 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'>

Som du kan se, ikke Python ikke vurdere b () annerledes enn en (). I Python alle metoder er like variabler som måtte være funksjoner.

Svarte 22/08/2008 kl. 14:40
kilden bruker

stemmer
2

I finner det underlig at ingen nevnes at alle de metoder som er nevnt ovenfor danner en syklus referanse mellom den tilsatte metode og forekomsten, slik at objektet som skal vedvarende inntil sanering. Det var et gammelt triks å legge en descriptor ved å utvide klassen av objektet:

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass
Svarte 30/04/2017 kl. 04:57
kilden bruker

stemmer
2

Dette spørsmålet ble åpnet år siden, men hei, det er en enkel måte å simulere bindingen av en funksjon til en klasse forekomst ved hjelp dekoratører:

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 ()

Der når du passerer funksjon og forekomsten av bindemiddel dekoratør, vil det skape en ny funksjon, med den samme koden objekt som den første. Deretter, blir det gitt forekomst av klassen som er lagret i en egenskap av det nylig opprettede funksjon. Dekoratør returnere en (tredje) funksjon ringer automatisk den kopierte funksjon, noe som gir det eksempel som den første parameter.

I konklusjonen får du en funksjon som simulerer det er binding til klassen forekomst. Å la den opprinnelige funksjonen uendret.

Svarte 21/12/2015 kl. 21:39
kilden bruker

stemmer
1
from types import MethodType

def method(self):
   print 'hi!'


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

Med denne kan du bruke selvpekeren

Svarte 27/07/2017 kl. 04:21
kilden bruker

stemmer
-8

Jeg vet ikke Python syntaks, men jeg vet at Ruby kan gjøre det, og det er ganske trivielt. La oss si at du ønsker å legge til en metode for å Array som skriver lengden til standard ut:

class Array
  def print_length
    puts length
  end
end

Hvis du ikke vil endre hele klassen, kan du bare legge metoden til en enkelt forekomst av tabellen, og ingen andre grupper vil ha metoden:

array = [1, 2, 3]
def array.print_length
  puts length
end

Bare vær klar over problemene som er involvert i å bruke denne funksjonen. Jeff Atwood faktisk skrev om det ikke altfor lenge siden.

Svarte 04/08/2008 kl. 03:36
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more