Apakah ada fungsi bawaan untuk mencetak semua properti dan nilai saat ini dari suatu objek?


Jawaban:


591

Anda benar-benar mencampurkan dua hal yang berbeda.

Gunakan dir(), vars()atau inspectmodul untuk mendapatkan apa yang Anda minati (saya gunakan __builtins__sebagai contoh; Anda bisa menggunakan objek apa saja).

>>> l = dir(__builtins__)
>>> d = __builtins__.__dict__

Cetak kamus itu sesuka Anda:

>>> print l
['ArithmeticError', 'AssertionError', 'AttributeError',...

atau

>>> from pprint import pprint
>>> pprint(l)
['ArithmeticError',
 'AssertionError',
 'AttributeError',
 'BaseException',
 'DeprecationWarning',
...

>>> pprint(d, indent=2)
{ 'ArithmeticError': <type 'exceptions.ArithmeticError'>,
  'AssertionError': <type 'exceptions.AssertionError'>,
  'AttributeError': <type 'exceptions.AttributeError'>,
...
  '_': [ 'ArithmeticError',
         'AssertionError',
         'AttributeError',
         'BaseException',
         'DeprecationWarning',
...

Pencetakan cantik juga tersedia di debugger interaktif sebagai perintah:

(Pdb) pp vars()
{'__builtins__': {'ArithmeticError': <type 'exceptions.ArithmeticError'>,
                  'AssertionError': <type 'exceptions.AssertionError'>,
                  'AttributeError': <type 'exceptions.AttributeError'>,
                  'BaseException': <type 'exceptions.BaseException'>,
                  'BufferError': <type 'exceptions.BufferError'>,
                  ...
                  'zip': <built-in function zip>},
 '__file__': 'pass.py',
 '__name__': '__main__'}

28
Anehnya, tampaknya tidak semua objek memiliki __dict__anggota ( re.MatchObjectmisalnya), tetapi builtin dir()berfungsi untuk semua objek.
hobs

print re.compile(r'slots').search('No slots here either.').__slots__
hobs

3
Yang baru bagiku. Terima kasih. Titik itu memicu parser jalur modul otak saya. Bahkan tidak pernah dianggap sebagai "modul" Latin.
Hobs

4
mengapa Anda tidak berbicara lebih banyak tentang inspectmodul dalam jawaban Anda? Saya pikir itu adalah hal yang paling dekat dengan print_r atau var_dump.
Hai Phaikawl

1
Bagaimana Anda mengakses nilai di balik atribut yang terdaftar oleh dir(), lalu? dir()hanya mengembalikan daftar nama, dan tidak semua yang ada di vars()atau dalam __dict__atribut.
HelloGoodbye

981

Anda ingin vars()dicampur dengan pprint():

from pprint import pprint
pprint(vars(your_object))

24
vars()hanya mengembalikan __dict__argumennya dan itu juga mundur dir()jika tidak ada __dir__metode. jadi gunakan dir()di tempat pertama, seperti yang saya katakan.

28
@hop: dir()memberi Anda semua hal bawaan yang mungkin tidak Anda sukai __str__dan __new__. var()tidak.
Timmmm

14
Ini gagal pada set dan objek lain yang tidak memiliki __dict__atribut.
anatoly techtonik

209
def dump(obj):
  for attr in dir(obj):
    print("obj.%s = %r" % (attr, getattr(obj, attr)))

Ada banyak fungsi pihak ke-3 di luar sana yang menambahkan hal-hal seperti penanganan pengecualian, pencetakan karakter nasional / khusus, berulang ke objek bersarang dll sesuai dengan preferensi penulisnya. Tapi mereka semua pada dasarnya bermuara pada hal ini.


5
unpythonic, karena mengikuti tidak-ditemukan-di sini

14
Katakan apa? Tentu, Anda dapat menggunakan getmembers()fungsi dalam inspectmodul standar , tetapi saya pikir ini akan lebih berguna karena menggambarkan bagaimana melakukan introspeksi secara umum.
Dan Lenski

20
TIDAK SEMUANYA. dir (obj) menunjukkan properti yang tidak ditemukan di __dict__(seperti __doc__dan __module__). Selain itu, __dict__tidak berfungsi sama sekali untuk objek yang dideklarasikan __slots__. Secara umum, __dict__memperlihatkan properti tingkat pengguna yang sebenarnya disimpan dalam kamus secara internal. dir () menunjukkan lebih banyak.
Dan Lenski

8
Beberapa kelas / objek tidak mengandung __dict__atribut / anggota. Saya tahu ini gila, tetapi benar. Built-in like intdan stratau re.MatchObjects adalah contoh umum. Coba 'hello'.__dict__, lalu cobadir('hello')
hobs

11
Saya tidak peduli apakah itu «unpythonic» atau yang lainnya. Ini menyelesaikan pekerjaan, yang dalam debugging adalah satu-satunya hal yang penting.
hidefromkgb

59

dir telah disebutkan, tetapi itu hanya akan memberi Anda nama atribut. Jika Anda ingin nilainya juga coba __dict__.

class O:
   def __init__ (self):
      self.value = 3

o = O()

Berikut hasilnya:

>>> o.__dict__

{'value': 3}

9
Benda seperti settidak memiliki __dict__, jadi bagi mereka itu akan gagal denganAttributeError: 'set' object has no attribute '__dict__'
anatoly techtonik

23

Anda dapat menggunakan fungsi "dir ()" untuk melakukan ini.

>>> import sys
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__', '__stdin__', '__stdo
t__', '_current_frames', '_getframe', 'api_version', 'argv', 'builtin_module_names', 'byteorder
, 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'exc_clear', 'exc_info'
 'exc_type', 'excepthook', 'exec_prefix', 'executable', 'exit', 'getcheckinterval', 'getdefault
ncoding', 'getfilesystemencoding', 'getrecursionlimit', 'getrefcount', 'getwindowsversion', 'he
version', 'maxint', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_
ache', 'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setprofile', 'setrecursionlimit
, 'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_info', 'warnoption
', 'winver']
>>>

Fitur lain yang bermanfaat adalah bantuan.

>>> help(sys)
Help on built-in module sys:

NAME
    sys

FILE
    (built-in)

MODULE DOCS
    http://www.python.org/doc/current/lib/module-sys.html

DESCRIPTION
    This module provides access to some objects used or maintained by the
    interpreter and to functions that interact strongly with the interpreter.

    Dynamic objects:

    argv -- command line arguments; argv[0] is the script pathname if known

22

Untuk mencetak keadaan objek saat ini, Anda mungkin:

>>> obj # in an interpreter

atau

print repr(obj) # in a script

atau

print obj

Untuk kelas Anda tentukan __str__atau __repr__metode. Dari dokumentasi Python :

__repr__(self)Dipanggil oleh repr()fungsi bawaan dan oleh konversi string (tanda kutip terbalik) untuk menghitung representasi string "resmi" dari suatu objek. Jika memungkinkan, ini akan terlihat seperti ekspresi Python yang valid yang dapat digunakan untuk membuat ulang objek dengan nilai yang sama (diberikan lingkungan yang sesuai). Jika ini tidak memungkinkan, string bentuk "<... beberapa deskripsi bermanfaat ...>" harus dikembalikan. Nilai kembali harus berupa objek string. Jika kelas mendefinisikan repr () tetapi tidak __str__(), maka __repr__()juga digunakan ketika representasi string "informal" dari instance kelas itu diperlukan. Ini biasanya digunakan untuk debugging, jadi penting agar representasi kaya informasi dan tidak ambigu.

__str__(self)Dipanggil oleh str()fungsi bawaan dan oleh pernyataan cetak untuk menghitung representasi string "informal" dari suatu objek. Ini berbeda dari __repr__()yang tidak harus ekspresi Python yang valid: representasi yang lebih nyaman atau ringkas dapat digunakan sebagai gantinya. Nilai kembali harus berupa objek string.


Opsi ini berguna untuk mencetak string yang digabungkan dengan konten objek:print "DEBUG: object value: " + repr(obj)
AlejandroVD

17

Mungkin patut dicoba -

Apakah ada Python yang setara dengan Data Perl :: Dumper?

Rekomendasi saya adalah ini -

https://gist.github.com/1071857

Perhatikan bahwa perl memiliki modul bernama Data :: Dumper yang menerjemahkan data objek kembali ke kode sumber perl (NB: TIDAK menerjemahkan kode kembali ke sumber, dan hampir selalu Anda tidak ingin fungsi metode objek di output). Ini dapat digunakan untuk kegigihan, tetapi tujuan umumnya adalah untuk debugging.

Ada beberapa hal yang tidak dapat dicapai oleh jejak python standar, khususnya ia hanya berhenti turun ketika ia melihat instance objek dan memberi Anda pointer hex internal objek (errr, pointer itu tidak banyak digunakan oleh jalan). Jadi singkatnya, python adalah semua tentang paradigma berorientasi objek yang hebat ini, tetapi alat yang Anda dapatkan di luar kotak dirancang untuk bekerja dengan sesuatu selain objek.

Data perl :: Dumper memungkinkan Anda untuk mengontrol seberapa dalam Anda ingin pergi, dan juga mendeteksi struktur yang ditautkan melingkar (itu benar-benar penting). Proses ini secara fundamental lebih mudah untuk dicapai dalam perl karena objek tidak memiliki sihir khusus di luar berkat mereka (proses yang didefinisikan secara universal).


Ini harus menjadi pip dan deb tidak hanya inti!
phobie

2
> Jadi singkatnya, python adalah semua tentang paradigma berorientasi objek yang hebat ini, tetapi alat yang Anda peroleh dari kotak dirancang untuk bekerja dengan sesuatu selain objek ... Klaim yang cukup ketika satu-satunya contoh yang Anda berikan adalah modul kepentingan sekunder.
memeplex

@memeplex di mana dikatakan python adalah semua tentang OOP?
Peter Wood

Ok, itu hanya mengatakan ini semua tentang OOP besar ini , salahku.
memeplex

13

Saya sarankan menggunakan help(your_object).

help(dir)

 If called without an argument, return the names in the current scope.
 Else, return an alphabetized list of names comprising (some of) the attributes
 of the given object, and of attributes reachable from it.
 If the object supplies a method named __dir__, it will be used; otherwise
 the default dir() logic is used and returns:
 for a module object: the module's attributes.
 for a class object:  its attributes, and recursively the attributes
 of its bases.
 for any other object: its attributes, its class's attributes, and
 recursively the attributes of its class's base classes.

help(vars)

Without arguments, equivalent to locals().
With an argument, equivalent to object.__dict__.

Oh, kawan, +100500
Kirby

12

Dalam kebanyakan kasus, menggunakan __dict__atau dir()akan memberi Anda info yang Anda inginkan. Jika Anda membutuhkan lebih banyak detail, pustaka standar menyertakan modul inspect , yang memungkinkan Anda untuk mendapatkan sejumlah detail yang mengesankan. Beberapa saran nyata dari info termasuk:

  • nama parameter fungsi dan metode
  • hierarki kelas
  • kode sumber implementasi objek fungsi / kelas
  • variabel lokal keluar dari objek bingkai

Jika Anda hanya mencari "nilai atribut apa yang dimiliki objek saya?", Maka dir()dan __dict__mungkin cukup. Jika Anda benar-benar ingin menggali keadaan saat ini dari objek yang berubah-ubah (mengingat bahwa hampir semua objek adalah python), maka inspectlayak untuk dipertimbangkan.


Gunakan penjelasan Anda tentang pemeriksaan untuk meningkatkan jawaban yang paling lengkap. Semoga tidak apa-apa denganmu.
Fernando César

10

Apakah ada fungsi bawaan untuk mencetak semua properti dan nilai saat ini dari suatu objek?

Tidak. Jawaban yang paling banyak dipilih tidak menyertakan beberapa atribut, dan jawaban yang diterima menunjukkan cara mendapatkan semua atribut, termasuk metode dan bagian dari api non-publik. Tapi tidak ada builtin lengkap yang bagus fungsi untuk ini.

Jadi akibat singkatnya adalah Anda bisa menulis sendiri, tetapi akan menghitung properti dan deskriptor data yang dihitung yang merupakan bagian dari API publik, dan Anda mungkin tidak menginginkan itu:

from pprint import pprint
from inspect import getmembers
from types import FunctionType

def attributes(obj):
    disallowed_names = {
      name for name, value in getmembers(type(obj)) 
        if isinstance(value, FunctionType)}
    return {
      name: getattr(obj, name) for name in dir(obj) 
        if name[0] != '_' and name not in disallowed_names and hasattr(obj, name)}

def print_attributes(obj):
    pprint(attributes(obj))

Masalah dengan jawaban lain

Amati aplikasi jawaban terpilih saat ini di kelas dengan banyak jenis data anggota:

from pprint import pprint

class Obj:
    __slots__ = 'foo', 'bar', '__dict__'
    def __init__(self, baz):
        self.foo = ''
        self.bar = 0
        self.baz = baz
    @property
    def quux(self):
        return self.foo * self.bar

obj = Obj('baz')
pprint(vars(obj))

hanya cetak:

{'baz': 'baz'}

Karena vars hanya mengembalikan __dict__objek, dan itu bukan salinan, jadi jika Anda memodifikasi dict yang dikembalikan oleh vars, Anda juga memodifikasi __dict__objek itu sendiri.

vars(obj)['quux'] = 'WHAT?!'
vars(obj)

pengembalian:

{'baz': 'baz', 'quux': 'WHAT?!'}

- yang buruk karena quux adalah properti yang seharusnya tidak kita atur dan tidak boleh berada di namespace ...

Menerapkan saran dalam jawaban yang saat ini diterima (dan lainnya) tidak jauh lebih baik:

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'bar', 'baz', 'foo', 'quux']

Seperti yang dapat kita lihat, dirhanya mengembalikan semua (sebenarnya hanya sebagian besar) dari nama yang terkait dengan objek.

inspect.getmembers, disebutkan dalam komentar, juga cacat - mengembalikan semua nama dan nilai.

Dari kelas

Saat mengajar, saya meminta siswa saya membuat fungsi yang menyediakan API objek semantik yang publik:

def api(obj):
    return [name for name in dir(obj) if name[0] != '_']

Kita dapat memperluas ini untuk memberikan salinan namespace semantik dari suatu objek, tetapi kita perlu mengecualikan __slots__yang tidak ditugaskan, dan jika kita menanggapi permintaan "properti saat ini" dengan serius, kita perlu mengecualikan properti yang dihitung (seperti mereka bisa menjadi mahal, dan bisa diartikan sebagai bukan "saat ini"):

from types import FunctionType
from inspect import getmembers

def attrs(obj):
     disallowed_properties = {
       name for name, value in getmembers(type(obj)) 
         if isinstance(value, (property, FunctionType))}
     return {
       name: getattr(obj, name) for name in api(obj) 
         if name not in disallowed_properties and hasattr(obj, name)}

Dan sekarang kami tidak menghitung atau menampilkan properti, quux:

>>> attrs(obj)
{'bar': 0, 'baz': 'baz', 'foo': ''}

Peringatan

Tapi mungkin kita tahu properti kita tidak mahal. Kami mungkin ingin mengubah logika untuk memasukkan mereka juga. Dan mungkin kami ingin mengecualikan deskriptor data khusus lainnya .

Maka kita perlu menyesuaikan fungsi ini lebih lanjut. Maka masuk akal bahwa kita tidak dapat memiliki fungsi bawaan yang secara ajaib tahu persis apa yang kita inginkan dan menyediakannya. Ini adalah fungsi yang perlu kita ciptakan sendiri.

Kesimpulan

Tidak ada fungsi bawaan yang melakukan ini, dan Anda harus melakukan apa yang paling semantik sesuai dengan situasi Anda.


Ada kurung ekstra dekat setelahnya FunctionType. Tetapi sangat membantu - terima kasih!
nealmcb

@nealmcb terima kasih kurasa aku mengerti. Senang bisa melayani! :)
Aaron Hall

7

Contoh metaprogramming objek Dump dengan sihir :

$ cat dump.py
#!/usr/bin/python
import sys
if len(sys.argv) > 2:
    module, metaklass  = sys.argv[1:3]
    m = __import__(module, globals(), locals(), [metaklass])
    __metaclass__ = getattr(m, metaklass)

class Data:
    def __init__(self):
        self.num = 38
        self.lst = ['a','b','c']
        self.str = 'spam'
    dumps   = lambda self: repr(self)
    __str__ = lambda self: self.dumps()

data = Data()
print data

Tanpa argumen:

$ python dump.py
<__main__.Data instance at 0x00A052D8>

Dengan Utilitas Gnosis :

$ python dump.py gnosis.magic MetaXMLPickler
<?xml version="1.0"?>
<!DOCTYPE PyObject SYSTEM "PyObjects.dtd">
<PyObject module="__main__" class="Data" id="11038416">
<attr name="lst" type="list" id="11196136" >
  <item type="string" value="a" />
  <item type="string" value="b" />
  <item type="string" value="c" />
</attr>
<attr name="num" type="numeric" value="38" />
<attr name="str" type="string" value="spam" />
</PyObject>

Agak ketinggalan jaman tetapi masih berfungsi.


6

Jika Anda menggunakan ini untuk debugging, dan Anda hanya ingin dump rekursif dari semuanya, jawaban yang diterima tidak memuaskan karena mengharuskan kelas Anda sudah memiliki __str__implementasi yang baik . Jika bukan itu masalahnya, ini bekerja lebih baik:

import json
print(json.dumps(YOUR_OBJECT, 
                 default=lambda obj: vars(obj),
                 indent=1))

ini tidak berfungsi pada python 3. Harus menginstal pymongo dan melakukannya sesuai jawaban @Clark
Tim Ogilvy

seperti halnya dengan banyak jawaban lain di siniTypeError: vars() argument must have __dict__ attribute
merampok

6

Coba ppretty

from ppretty import ppretty


class A(object):
    s = 5

    def __init__(self):
        self._p = 8

    @property
    def foo(self):
        return range(10)


print ppretty(A(), show_protected=True, show_static=True, show_properties=True)

Keluaran:

__main__.A(_p = 8, foo = [0, 1, ..., 8, 9], s = 5)

persis apa yang saya cari untuk debug cepat :), temukan!
Joseph Astrahan

sedikit petunjuk tambahkan kedalaman = 6 (atau sejauh yang Anda butuhkan) sebagai salah satu parameter untuk itu dan detail rekursi bisa lebih jauh :). Salah satu hal yang saya sukai tentang cara mencetak daftar adalah menampilkan 2 entri pertama dan 2 entri terakhir sehingga Anda tahu itu berfungsi
Joseph Astrahan

4
from pprint import pprint

def print_r(the_object):
    print ("CLASS: ", the_object.__class__.__name__, " (BASE CLASS: ", the_object.__class__.__bases__,")")
    pprint(vars(the_object))

4

Ini mencetak semua konten objek secara rekursif dalam format indentasi json atau yaml:

import jsonpickle # pip install jsonpickle
import json
import yaml # pip install pyyaml

serialized = jsonpickle.encode(obj, max_depth=2) # max_depth is optional
print json.dumps(json.loads(serialized), indent=4)
print yaml.dump(yaml.load(serialized), indent=4)

4

Saya telah mengangkat jawaban yang hanya menyebutkan sidik jari. Untuk menjadi jelas, jika Anda ingin melihat semua nilai dalam struktur data yang kompleks, maka lakukan sesuatu seperti:

from pprint import pprint
pprint(my_var)

Di mana my_var adalah variabel yang Anda minati. Ketika saya menggunakan, pprint(vars(my_var))saya tidak mendapatkan apa-apa, dan jawaban lain di sini tidak membantu atau metode itu tampak terlalu lama. Ngomong-ngomong, dalam kasus khusus saya, kode yang saya periksa memiliki kamus kamus.

Layak menunjukkan bahwa dengan beberapa kelas khusus Anda mungkin berakhir dengan <someobject.ExampleClass object at 0x7f739267f400>jenis output yang tidak membantu . Dalam hal ini, Anda mungkin harus menerapkan __str__metode, atau mencoba beberapa solusi lain. Saya masih ingin menemukan sesuatu yang sederhana yang berfungsi di semua skenario, tanpa perpustakaan pihak ketiga.


3
> dengan beberapa kelas khusus ... Ini sebabnya saya bukan penggemar python. Hal-hal "kadang-kadang" berhasil dan "kadang-kadang" tidak
AlxVallejo

3

Saya perlu mencetak info DEBUG di beberapa log dan tidak dapat menggunakan pprint karena akan merusaknya. Sebaliknya saya melakukan ini dan mendapatkan hal yang hampir sama.

DO = DemoObject()

itemDir = DO.__dict__

for i in itemDir:
    print '{0}  :  {1}'.format(i, itemDir[i])

3

Untuk membuang "myObject":

from bson import json_util
import json

print(json.dumps(myObject, default=json_util.default, sort_keys=True, indent=4, separators=(',', ': ')))

Saya mencoba vars () dan dir (); keduanya gagal untuk apa yang saya cari. vars () tidak berfungsi karena objek tidak memiliki __dict__ (pengecualian .TypeError: vars () argumen harus memiliki atribut __dict__). dir () bukan yang saya cari: itu hanya daftar nama bidang, tidak memberikan nilai atau struktur objek.

Saya pikir json.dumps () akan bekerja untuk sebagian besar objek tanpa default = json_util.default, tetapi saya memiliki bidang datetime di objek sehingga serializer json gagal. Lihat Bagaimana mengatasi "datetime.datetime not JSON serializable" dengan python?


Oke ya harus menginstal pymongo tho untuk menggunakannya.
Tim Ogilvy

3

Mengapa bukan sesuatu yang sederhana:

for key,value in obj.__dict__.iteritems():
    print key,value

Bukankah seharusnya begitu for key,value in obj.__dict__.iteritems(): print key,value?
Raz

2

pprint berisi "printer cantik" untuk menghasilkan representasi struktur data Anda yang menyenangkan secara estetis. Formatter menghasilkan representasi struktur data yang dapat diurai dengan benar oleh penerjemah, dan juga mudah bagi manusia untuk dibaca. Output disimpan pada satu baris, jika mungkin, dan menjorok ketika terbagi beberapa baris.


2

Coba cetak ulang .

Ini akan membantu Anda tidak hanya dengan mencetak variabel objek, tetapi juga output yang indah, seperti ini:

class(NormalClassNewStyle):
  dicts: {
  },
  lists: [],
  static_props: 1,
  tupl: (1, 2)

1
Modul ini tampaknya tidak dipertahankan lagi dan memiliki sejumlah masalah terbuka. Alih-alih menggunakan ppretty
Wavesailor

1

Untuk semua orang yang berjuang

  • vars() tidak mengembalikan semua atribut.
  • dir() tidak mengembalikan nilai atribut.

Kode berikut mencetak semua atribut objdengan nilainya:

for attr in dir(obj):
        try:
            print("obj.{} = {}".format(attr, getattr(obj, attr)))
        except AttributeError:
            print("obj.{} = ?".format(attr))

tidak mendapatkan kesalahan, tetapi tidak rekursif sehingga hanya mendapatkan banyak alamat hex
rob

0

Anda dapat mencoba Flask Debug Toolbar.
https://pypi.python.org/pypi/Flask-DebugToolbar

from flask import Flask
from flask_debugtoolbar import DebugToolbarExtension

app = Flask(__name__)

# the toolbar is only enabled in debug mode:
app.debug = True

# set a 'SECRET_KEY' to enable the Flask session cookies
app.config['SECRET_KEY'] = '<replace with a secret key>'

toolbar = DebugToolbarExtension(app)

0

Saya suka bekerja dengan objek atau nilai - nilai kunci objek python .

Untuk atribut terlepas dari itu adalah metode atau variabel:

o.keys()

Untuk nilai atribut tersebut:

o.values()

0

Ini berfungsi tidak masalah bagaimana variabel Anda didefinisikan di dalam kelas, di dalam __init__ atau di luar.

your_obj = YourObj()
attrs_with_value = {attr: getattr(your_obj, attr) for attr in dir(your_obj)}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.