de-vraag
  • Pertanyaan
  • Tag
  • Pengguna
Notifikasi
Imbalan
Registrasi
Setelah Anda mendaftar, Anda akan diberitahu tentang balasan dan komentar untuk pertanyaan Anda.
Gabung
Jika Anda sudah memiliki akun, masuk untuk memeriksa pemberitahuan baru.
Akan ada hadiah untuk pertanyaan, jawaban, dan komentar tambahan.
Lebih
Sumber
Sunting
 carmellose
carmellose
Question

Cara terbaik untuk memeriksa argumen fungsi pada Python

I'm mencari cara yang efisien untuk memeriksa variabel python fungsi. Sebagai contoh, saya'd seperti untuk memeriksa argumen jenis dan nilai. Apakah ada modul untuk ini? Atau saya harus menggunakan sesuatu seperti dekorator, atau idiom tertentu?

def my_function(a, b, c):
    """an example function I'd like to check the arguments of."""
    # check that a is an int
    # check that 0 < b < 10
    # check that c is not an empty string
56 2013-10-30T14:03:34+00:00 14
Aviv Cohn
Aviv Cohn
Pertanyaan edit 22 Desember 2015 в 10:59
Pemrograman
python
function
arguments
Cecil Curry
Cecil Curry
22 Juni 2016 в 7:21
2016-06-22T07:21:23+00:00
Lebih
Sumber
Sunting
#22666972

Dalam hal ini memanjang menjawab, kami menerapkan Python 3.x-jenis tertentu memeriksa dekorator berdasarkan PEP 484-jenis gaya petunjuk dalam waktu kurang dari 275 garis-garis murni-Python (yang sebagian besar adalah penjelasan docstrings dan komentar) – sangat dioptimalkan untuk industri-kekuatan yang digunakan di dunia nyata lengkap dengan py.test-didorong test suite berolahraga semua kemungkinan kasus tepi. Pesta yang tak terduga yang mengagumkan dari bear mengetik:

>>> @beartype
... def spirit_bear(kermode: str, gitgaata: (str, int)) -> tuple:
...     return (kermode, gitgaata, "Moksgm'ol", 'Ursus americanus kermodei')
>>> spirit_bear(0xdeadbeef, 'People of the Cane')
AssertionError: parameter kermode=0xdeadbeef not of <class "str">

Sebagai contoh ini menunjukkan, beruang mengetik secara eksplisit mendukung jenis pemeriksaan parameter dan mengembalikan nilai-nilai yang dijelaskan sebagai jenis yang sederhana atau tupel dari jenis seperti. Golly! O. K., bahwa's benar-benar mengesankan. @beartype menyerupai setiap other Python 3.x-jenis tertentu memeriksa dekorator berdasarkan PEP 484-jenis gaya petunjuk dalam waktu kurang dari 275 garis-garis murni-Python. Jadi apa yang's menggosok, dada?

Murni Bruteforce Hardcore Efisiensi

Beruang mengetik secara dramatis lebih efisien dalam ruang dan waktu dari semua yang ada implementasi dari tipe check-in Python untuk yang terbaik dari saya yang terbatas domain pengetahuan. (More pada nanti.) Efisiensi biasanya doesn't peduli di Python, namun. Jika itu terjadi, anda tidak't dapat menggunakan Python. Apakah jenis pemeriksaan yang benar-benar menyimpang dari mapan norma menghindari dini optimasi di Python? Ya. Ya, itu tidak. Mempertimbangkan profil, yang menambahkan tidak dapat dihindari overhead untuk masing-masing profil metrik bunga (misalnya, panggilan fungsi, garis-garis). Untuk memastikan hasil yang akurat, overhead ini adalah dikurangi dengan memanfaatkan dioptimalkan C ekstensi (misalnya, _lsprof C ekstensi leveraged oleh cProfile modul) daripada unoptimized murni-Python (misalnya, profil modul). Efisiensi benar-benar does peduli ketika profiling. Jenis pemeriksaan ini tidak berbeda. Jenis pemeriksaan menambahkan overhead untuk masing-masing fungsi jenis panggilan diperiksa oleh aplikasi anda – idealnya, all dari mereka. Untuk mencegah bermaksud baik (tapi sayangnya kecil-minded) rekan kerja dari menghapus tipe memeriksa anda secara diam-diam ditambahkan setelah jumat lalu's kafein-kacau allnighter untuk anda geriatri warisan Django web app, jenis pemeriksaan harus cepat. Begitu cepat sehingga tidak ada pemberitahuan itu's ada ketika anda menambahkan itu tanpa memberitahu siapa pun. I melakukan hal ini sepanjang waktu! Berhenti membaca ini jika anda adalah rekan kerja. Jika bahkan menggelikan, kecepatan angin dan isn't cukup untuk anda rakus aplikasi, namun, beruang mengetik dapat dinonaktifkan secara global dengan memungkinkan Python optimasi (misalnya, dengan melewati -O pilihan untuk interpreter Python):

$ python3 -O
# This succeeds only when type checking is optimized away. See above!
>>> spirit_bear(0xdeadbeef, 'People of the Cane')
(0xdeadbeef, 'People of the Cane', "Moksgm'ol", 'Ursus americanus kermodei')

Hanya karena. Selamat datang untuk menanggung mengetik.

Apa...? Mengapa "bear"? Anda're Neckbeard, Kan?

Beruang mengetik telanjang-logam jenis pemeriksaan – yang, jenis pemeriksaan sebagai dekat dengan pendekatan manual dari tipe check-in Python sebagai layak. Beruang mengetik ini dimaksudkan untuk memaksakan no hukuman kinerja, kompatibilitas, kendala, atau pihak ketiga dependensi (atas dan di atas yang dikenakan oleh pendekatan manual, sih). Beruang mengetik dapat mulus diintegrasikan ke dalam ada codebases dan tes suites tanpa modifikasi. Semua orang's mungkin akrab dengan pendekatan manual. Anda secara manual menegaskan masing-masing parameter yang dilewatkan ke dan/atau pengembalian nilai yang dihasilkan dari every fungsi dalam basis kode. Apa yang boilerplate bisa menjadi lebih sederhana atau lebih dangkal? Kami've melihat semua itu seratus kali googleplex kali, dan muntah di mulut kita setiap kali kita lakukan. Pengulangan akan cepat tua. DRY, yo. Mendapatkan muntah tas siap. Untuk singkatnya, let's mengasumsikan modern easy_spirit_bear() fungsi menerima hanya satu str parameter. Berikut ini's apa yang anda benar-benar tidak terlihat seperti:

def easy_spirit_bear(kermode: str) -> str:
    assert isinstance(kermode, str), 'easy_spirit_bear() parameter kermode={} not of <class "str">'.format(kermode)
    return_value = (kermode, "Moksgm'ol", 'Ursus americanus kermodei')
    assert isinstance(return_value, str), 'easy_spirit_bear() return value {} not of <class "str">'.format(return_value)
    return return_value

Python 101, kan? Banyak dari kita melewati kelas itu. Beruang mengetik ekstrak jenis pemeriksaan manual yang dilakukan dengan pendekatan di atas menjadi dinamis didefinisikan pembungkus fungsi yang secara otomatis melakukan pemeriksaan yang sama – dengan manfaat tambahan dari beternak granular TypeError daripada ambigu AssertionError pengecualian. Berikut ini's apa otomatis pendekatan terlihat seperti:

def easy_spirit_bear_wrapper(*args, __beartype_func=easy_spirit_bear, **kwargs):
    if not (
        isinstance(args[0], __beartype_func.__annotations__['kermode'])
        if 0 < len(args) else
        isinstance(kwargs['kermode'], __beartype_func.__annotations__['kermode'])
        if 'kermode' in kwargs else True):
            raise TypeError(
                'easy_spirit_bear() parameter kermode={} not of {!r}'.format(
                args[0] if 0 < len(args) else kwargs['kermode'],
                __beartype_func.__annotations__['kermode']))

    return_value = __beartype_func(*args, **kwargs)

    if not isinstance(return_value, __beartype_func.__annotations__['return']):
        raise TypeError(
            'easy_spirit_bear() return value {} not of {!r}'.format(
                return_value, __beartype_func.__annotations__['return']))

    return return_value

It's bertele-tele. Tapi itu's juga pada dasarnya* secepat pendekatan manual. * Squinting yang disarankan.

Catatan kurang lengkap fungsi pemeriksaan atau iterasi dalam pembungkus fungsi, yang mengandung jumlah yang sama dari tes sebagai fungsi asli – meskipun dengan tambahan (mungkin diabaikan) biaya pengujian apakah dan bagaimana parameter tipe diperiksa dilewatkan saat pemanggilan fungsi. Anda dapat't memenangkan setiap pertempuran. Bisa seperti fungsi pembungkus actually menjadi andal yang dihasilkan untuk jenis check fungsi sewenang-wenang dalam waktu kurang dari 275 baris Python murni? Ular Plisskin mengatakan, "True story. Punya rokok?" Dan, ya. Saya mungkin memiliki neckbeard.

Tidak, Srsly. Mengapa "bear"?

Beruang beats bebek. Bebek bisa terbang, tapi beruang dapat membuang salmon di bebek. In Kanada, alam dapat mengejutkan anda. Pertanyaan berikutnya.

Apa's Begitu Panas tentang Beruang, Sih?

Ada solusi tidak not melakukan bare-metal type checking – setidaknya, tidak ada I've grepped di. Mereka semua iteratif reinspect tanda tangan dari jenis-diperiksa pada fungsi setiap pemanggilan fungsi. Sementara diabaikan untuk satu panggilan, inspeksi ulang overhead biasanya non-diabaikan ketika diagregasikan semua panggilan. Really, really non-diabaikan. It's tidak hanya menyangkut efisiensi, namun. Solusi yang ada juga sering gagal untuk memperhitungkan umum edge kasus. Ini termasuk sebagian besar, jika tidak semua mainan dekorator yang disediakan seperti stackoverflow jawaban di sini dan di tempat lain. Klasik kegagalan meliputi:

  • Gagal untuk jenis cek kata kunci argumen dan/atau kembali nilai-nilai (misalnya, sweeneyrod's @checkargs dekorator).
  • Gagal untuk mendukung tupel (yaitu, serikat pekerja) dari jenis yang diterima oleh isinstance() builtin.
  • Gagal untuk menyebarkan nama, docstring, dan identitas lainnya metadata asli dari fungsi ke fungsi wrapper.
  • Gagal untuk menyediakan setidaknya kemiripan unit tes. (Kind kritis.)
  • Meningkatkan generik AssertionError pengecualian daripada spesifik TypeError pengecualian pada gagal jenis pemeriksaan. Untuk perincian dan kewarasan, jenis pemeriksaan harus never menaikkan generik pengecualian. Beruang mengetik berhasil di mana non-beruang gagal. Semua orang, semua beruang!

    Beruang Mengetik Unbared

    Beruang mengetik pergeseran ruang dan waktu biaya memeriksa fungsi tanda tangan dari pemanggilan fungsi waktu untuk definisi fungsi waktu – artinya, dari bungkusnya fungsi yang dikembalikan oleh @beartype dekorator menjadi dekorator itu sendiri. Sejak dekorator hanya disebut sekali per definisi fungsi, optimasi ini hasil gembira untuk semua. Beruang mengetik adalah upaya untuk memeriksa jenis kue dan memakannya juga. Untuk melakukannya, @beartype:

  1. Memeriksa tanda tangan dan penjelasan dari fungsi asli.
  2. Secara dinamis membangun tubuh pembungkus fungsi jenis pemeriksaan fungsi asli. Thaaat's benar. Kode Python menghasilkan kode Python.
  3. Secara dinamis menyatakan pembungkus fungsi melalui exec() builtin.
  4. Pengembalian ini wrapper fungsi. Akan kita? Let's menyelam ke dalam.
# If the active Python interpreter is *NOT* optimized (e.g., option "-O" was
# *NOT* passed to this interpreter), enable type checking.
if __debug__:
    import inspect
    from functools import wraps
    from inspect import Parameter, Signature

    def beartype(func: callable) -> callable:
        '''
        Decorate the passed **callable** (e.g., function, method) to validate
        both all annotated parameters passed to this callable _and_ the
        annotated value returned by this callable if any.

        This decorator performs rudimentary type checking based on Python 3.x
        function annotations, as officially documented by PEP 484 ("Type
        Hints"). While PEP 484 supports arbitrarily complex type composition,
        this decorator requires _all_ parameter and return value annotations to
        be either:

        * Classes (e.g., `int`, `OrderedDict`).
        * Tuples of classes (e.g., `(int, OrderedDict)`).

        If optimizations are enabled by the active Python interpreter (e.g., due
        to option `-O` passed to this interpreter), this decorator is a noop.

        Raises
        ----------
        NameError
            If any parameter has the reserved name `__beartype_func`.
        TypeError
            If either:
            * Any parameter or return value annotation is neither:
              * A type.
              * A tuple of types.
            * The kind of any parameter is unrecognized. This should _never_
              happen, assuming no significant changes to Python semantics.
        '''

        # Raw string of Python statements comprising the body of this wrapper,
        # including (in order):
        #
        # * A "@wraps" decorator propagating the name, docstring, and other
        #   identifying metadata of the original function to this wrapper.
        # * A private "__beartype_func" parameter initialized to this function.
        #   In theory, the "func" parameter passed to this decorator should be
        #   accessible as a closure-style local in this wrapper. For unknown
        #   reasons (presumably, a subtle bug in the exec() builtin), this is
        #   not the case. Instead, a closure-style local must be simulated by
        #   passing the "func" parameter to this function at function
        #   definition time as the default value of an arbitrary parameter. To
        #   ensure this default is *NOT* overwritten by a function accepting a
        #   parameter of the same name, this edge case is tested for below.
        # * Assert statements type checking parameters passed to this callable.
        # * A call to this callable.
        # * An assert statement type checking the value returned by this
        #   callable.
        #
        # While there exist numerous alternatives (e.g., appending to a list or
        # bytearray before joining the elements of that iterable into a string),
        # these alternatives are either slower (as in the case of a list, due to
        # the high up-front cost of list construction) or substantially more
        # cumbersome (as in the case of a bytearray). Since string concatenation
        # is heavily optimized by the official CPython interpreter, the simplest
        # approach is (curiously) the most ideal.
        func_body = '''
@wraps(__beartype_func)
def func_beartyped(*args, __beartype_func=__beartype_func, **kwargs):
'''

        # "inspect.Signature" instance encapsulating this callable's signature.
        func_sig = inspect.signature(func)

        # Human-readable name of this function for use in exceptions.
        func_name = func.__name__ + '()'

        # For the name of each parameter passed to this callable and the
        # "inspect.Parameter" instance encapsulating this parameter (in the
        # passed order)...
        for func_arg_index, func_arg in enumerate(func_sig.parameters.values()):
            # If this callable redefines a parameter initialized to a default
            # value by this wrapper, raise an exception. Permitting this
            # unlikely edge case would permit unsuspecting users to
            # "accidentally" override these defaults.
            if func_arg.name == '__beartype_func':
                raise NameError(
                    'Parameter {} reserved for use by @beartype.'.format(
                        func_arg.name))

            # If this parameter is both annotated and non-ignorable for purposes
            # of type checking, type check this parameter.
            if (func_arg.annotation is not Parameter.empty and
                func_arg.kind not in _PARAMETER_KIND_IGNORED):
                # Validate this annotation.
                _check_type_annotation(
                    annotation=func_arg.annotation,
                    label='{} parameter {} type'.format(
                        func_name, func_arg.name))

                # String evaluating to this parameter's annotated type.
                func_arg_type_expr = (
                    '__beartype_func.__annotations__[{!r}]'.format(
                        func_arg.name))

                # String evaluating to this parameter's current value when
                # passed as a keyword.
                func_arg_value_key_expr = 'kwargs[{!r}]'.format(func_arg.name)

                # If this parameter is keyword-only, type check this parameter
                # only by lookup in the variadic "**kwargs" dictionary.
                if func_arg.kind is Parameter.KEYWORD_ONLY:
                    func_body += '''
    if {arg_name!r} in kwargs and not isinstance(
        {arg_value_key_expr}, {arg_type_expr}):
        raise TypeError(
            '{func_name} keyword-only parameter '
            '{arg_name}={{}} not a {{!r}}'.format(
                {arg_value_key_expr}, {arg_type_expr}))
'''.format(
                        func_name=func_name,
                        arg_name=func_arg.name,
                        arg_type_expr=func_arg_type_expr,
                        arg_value_key_expr=func_arg_value_key_expr,
                    )
                # Else, this parameter may be passed either positionally or as
                # a keyword. Type check this parameter both by lookup in the
                # variadic "**kwargs" dictionary *AND* by index into the
                # variadic "*args" tuple.
                else:
                    # String evaluating to this parameter's current value when
                    # passed positionally.
                    func_arg_value_pos_expr = 'args[{!r}]'.format(
                        func_arg_index)

                    func_body += '''
    if not (
        isinstance({arg_value_pos_expr}, {arg_type_expr})
        if {arg_index} < len(args) else
        isinstance({arg_value_key_expr}, {arg_type_expr})
        if {arg_name!r} in kwargs else True):
            raise TypeError(
                '{func_name} parameter {arg_name}={{}} not of {{!r}}'.format(
                {arg_value_pos_expr} if {arg_index} < len(args) else {arg_value_key_expr},
                {arg_type_expr}))
'''.format(
                    func_name=func_name,
                    arg_name=func_arg.name,
                    arg_index=func_arg_index,
                    arg_type_expr=func_arg_type_expr,
                    arg_value_key_expr=func_arg_value_key_expr,
                    arg_value_pos_expr=func_arg_value_pos_expr,
                )

        # If this callable's return value is both annotated and non-ignorable
        # for purposes of type checking, type check this value.
        if func_sig.return_annotation not in _RETURN_ANNOTATION_IGNORED:
            # Validate this annotation.
            _check_type_annotation(
                annotation=func_sig.return_annotation,
                label='{} return type'.format(func_name))

            # Strings evaluating to this parameter's annotated type and
            # currently passed value, as above.
            func_return_type_expr = (
                "__beartype_func.__annotations__['return']")

            # Call this callable, type check the returned value, and return this
            # value from this wrapper.
            func_body += '''
    return_value = __beartype_func(*args, **kwargs)
    if not isinstance(return_value, {return_type}):
        raise TypeError(
            '{func_name} return value {{}} not of {{!r}}'.format(
                return_value, {return_type}))
    return return_value
'''.format(func_name=func_name, return_type=func_return_type_expr)
        # Else, call this callable and return this value from this wrapper.
        else:
            func_body += '''
    return __beartype_func(*args, **kwargs)
'''

        # Dictionary mapping from local attribute name to value. For efficiency,
        # only those local attributes explicitly required in the body of this
        # wrapper are copied from the current namespace. (See below.)
        local_attrs = {'__beartype_func': func}

        # Dynamically define this wrapper as a closure of this decorator. For
        # obscure and presumably uninteresting reasons, Python fails to locally
        # declare this closure when the locals() dictionary is passed; to
        # capture this closure, a local dictionary must be passed instead.
        exec(func_body, globals(), local_attrs)

        # Return this wrapper.
        return local_attrs['func_beartyped']

    _PARAMETER_KIND_IGNORED = {
        Parameter.POSITIONAL_ONLY, Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD,
    }
    '''
    Set of all `inspect.Parameter.kind` constants to be ignored during
    annotation- based type checking in the `@beartype` decorator.

    This includes:

    * Constants specific to variadic parameters (e.g., `*args`, `**kwargs`).
      Variadic parameters cannot be annotated and hence cannot be type checked.
    * Constants specific to positional-only parameters, which apply to non-pure-
      Python callables (e.g., defined by C extensions). The `@beartype`
      decorator applies _only_ to pure-Python callables, which provide no
      syntactic means of specifying positional-only parameters.
    '''

    _RETURN_ANNOTATION_IGNORED = {Signature.empty, None}
    '''
    Set of all annotations for return values to be ignored during annotation-
    based type checking in the `@beartype` decorator.

    This includes:

    * `Signature.empty`, signifying a callable whose return value is _not_
      annotated.
    * `None`, signifying a callable returning no value. By convention, callables
      returning no value are typically annotated to return `None`. Technically,
      callables whose return values are annotated as `None` _could_ be
      explicitly checked to return `None` rather than a none-`None` value. Since
      return values are safely ignorable by callers, however, there appears to
      be little real-world utility in enforcing this constraint.
    '''

    def _check_type_annotation(annotation: object, label: str) -> None:
        '''
        Validate the passed annotation to be a valid type supported by the
        `@beartype` decorator.

        Parameters
        ----------
        annotation : object
            Annotation to be validated.
        label : str
            Human-readable label describing this annotation, interpolated into
            exceptions raised by this function.

        Raises
        ----------
        TypeError
            If this annotation is neither a new-style class nor a tuple of
            new-style classes.
        '''

        # If this annotation is a tuple, raise an exception if any member of
        # this tuple is not a new-style class. Note that the "__name__"
        # attribute tested below is not defined by old-style classes and hence
        # serves as a helpful means of identifying new-style classes.
        if isinstance(annotation, tuple):
            for member in annotation:
                if not (
                    isinstance(member, type) and hasattr(member, '__name__')):
                    raise TypeError(
                        '{} tuple member {} not a new-style class'.format(
                            label, member))
        # Else if this annotation is not a new-style class, raise an exception.
        elif not (
            isinstance(annotation, type) and hasattr(annotation, '__name__')):
            raise TypeError(
                '{} {} neither a new-style class nor '
                'tuple of such classes'.format(label, annotation))

# Else, the active Python interpreter is optimized. In this case, disable type
# checking by reducing this decorator to the identity decorator.
else:
    def beartype(func: callable) -> callable:
        return func

Dan leycec mengatakan, Biarkan @beartype mendatangkan jenis pemeriksaan cepatnya: dan itu jadi.

Peringatan, Kutukan, dan janji-Janji Kosong

Tidak ada yang sempurna. Even beruang mengetik.

Peringatan I: Nilai Default Dicentang

Beruang mengetik tidak not jenis check unpassed parameter diberi nilai default. Dalam teori, itu bisa. Tapi tidak 275 garis atau kurang dan tentu saja tidak seperti stackoverflow menjawab. Aman (...probably benar-benar unsafe) asumsi adalah bahwa fungsi pelaksana mengklaim mereka tahu apa yang mereka lakukan ketika mereka mendefinisikan nilai default. Karena nilai-nilai default yang biasanya konstanta (...they'd lebih baik!), mengecek kembali jenis konstanta yang tidak pernah berubah pada masing-masing fungsi melakukan panggilan satu atau lebih nilai-nilai default akan bertentangan dengan prinsip dasar beruang mengetik: "Don't ulangi diri atas dan oooover dan oooo-oooover lagi." Tunjukkan yang salah dan aku akan mandi anda dengan upvotes.

Peringatan II: Tidak ada PEP 484

PEP 484 ("Jenis Petunjuk") meresmikan penggunaan fungsi anotasi pertama kali diperkenalkan oleh PEP 3107 ("Fungsi Anotasi"). Python 3.5 dangkal mendukung formalisasi ini dengan yang baru tingkat atas mengetik module, sebuah standar API untuk menyusun sewenang-wenang kompleks jenis dari sederhana jenis (misalnya, Callable[[Arg1Type, Arg2Type], ReturnType], jenis menggambarkan suatu fungsi yang menerima dua argumen dari jenis Arg1Type dan Arg2Type dan mengembalikan sebuah nilai dari tipe ReturnType). Beruang mengetik mendukung tidak satupun dari mereka. Dalam teori, itu bisa. Tapi tidak 275 garis atau kurang dan tentu saja tidak seperti stackoverflow menjawab. Beruang mengetik, namun, dukungan serikat jenis dalam cara yang sama bahwa isinstance() builtin mendukung serikat jenis: sebagai tupel. Ini dangkal sesuai dengan mengetik.Uni tipe – dengan jelas peringatan bahwa mengetik.Uni mendukung sewenang-wenang kompleks jenis, sementara tupel diterima oleh @beartype dukungan only sederhana kelas. Dalam pembelaan saya, 275 garis.

Tes atau Tidak't Terjadi

Berikut ini's inti itu. Mendapatkannya, gist? I'll berhenti sekarang. Seperti dengan @beartype dekorator itu sendiri, ini py.test tes dapat mulus diintegrasikan ke dalam ada tes suites tanpa modifikasi. Mulia, isn't itu? Sekarang wajib neckbeard kata-kata kasar tidak ada yang meminta.

Sejarah Kekerasan API

Python 3.5 tidak memberikan dukungan yang sebenarnya untuk menggunakan PEP 484 jenis. wat? It's benar: tidak ada jenis pemeriksaan, tidak ada jenis kesimpulan, tidak ada jenis nuthin'. Sebaliknya, pengembang diharapkan untuk rutin menjalankan seluruh codebases melalui heavyweight pihak ketiga CPython penerjemah pembungkus melaksanakan faksimili dukungan tersebut (misalnya, mypy). Tentu saja, ini bungkus memaksakan:

  • A kompatibilitas penalti. Sebagai official mypy FAQ mengakui dalam menanggapi pertanyaan yang sering diajukan "saya Bisa menggunakan mypy untuk jenis check saya yang sudah ada Python code?": "Itu tergantung. Kompatibilitas yang cukup baik, tetapi beberapa Python fitur yang belum dilaksanakan atau didukung sepenuhnya." selanjutnya FAQ response menjelaskan ketidakcocokan ini dengan menyatakan bahwa:
  • "...kode anda harus membuat atribut eksplisit dan menggunakan eksplisit protokol representasi." tata bahasa polisi melihat anda "yang eksplisit" dan meningkatkan anda implisit cemberut.
  • "Mypy akan mendukung modular, efisien jenis pemeriksaan, dan ini tampaknya mengesampingkan tipe memeriksa beberapa fitur bahasa, seperti sewenang-wenang runtime penambahan metode. Namun, sangat mungkin bahwa banyak dari fitur-fitur ini akan didukung dalam bentuk terbatas (misalnya, runtime modifikasi hanya didukung untuk kelas atau metode terdaftar sebagai dinamis atau 'patchable')."
  • Untuk daftar lengkap dari sintaksis yang tidak kompatibel, lihat "Berurusan dengan isu-isu umum". It's not cantik. Anda hanya ingin memeriksa jenis dan sekarang anda refactored seluruh basis kode dan memecahkan semua orang's membangun dua hari dari kandidat rilis dan cantik HR cebol dalam pakaian bisnis kasual slip merah muda slip melalui celah di bilik-cum-mancave. Terima kasih banyak, mypy.
  • A kinerja penalti, meskipun menafsirkan statis mengetik kode. Empat puluh tahun rebus komputer ilmu pengetahuan memberitahu kita bahwa (...semua lain yang equal) menafsirkan statis mengetik kode harus lebih cepat, bukan lebih lambat, dari menafsirkan secara dinamis mengetik kode. Di Python, hingga baru-baru ini turun.
  • Tambahan non-sepele ketergantungan, meningkatnya:
  • Bug-sarat kerapuhan proyek penyebaran, terutama cross-platform.
  • Pemeliharaan beban pengembangan proyek.
  • Kemungkinan serangan permukaan. Saya meminta Guido: "Mengapa? Mengapa repot-repot menciptakan abstrak API jika anda tidak't bersedia memberikannya beton API benar-benar melakukan sesuatu dengan abstraksi itu?" Mengapa meninggalkan nasib juta Pythonistas untuk rematik tangan gratis open-source pasar? Mengapa membuat lain techno-masalah yang bisa saja sepele diselesaikan dengan 275-line dekorator resmi Python stdlib? Saya tidak punya Python dan aku harus berteriak.
 Anentropic
Anentropic
Jawaban edit 31 Mei 2019 в 12:18
75
0
Solution / Answer
bruno desthuilliers
bruno desthuilliers
30 Oktober 2013 в 2:16
2013-10-30T14:16:03+00:00
Lebih
Sumber
Sunting
#22666968

Yang paling Pythonic idiom adalah untuk secara jelas document apa fungsi mengharapkan dan kemudian hanya mencoba untuk menggunakan apa pun yang dilewatkan ke fungsi dan baik membiarkan pengecualian menyebarkan atau hanya menangkap atribut kesalahan dan meningkatkan TypeError sebagai gantinya. Jenis pemeriksaan harus dihindari sebisa mungkin karena bertentangan duck-typing. Nilai pengujian dapat OK – tergantung pada konteks.

Satu-satunya tempat di mana validasi benar-benar masuk akal adalah di sistem atau subsistem entry point, seperti bentuk web, argumen baris perintah, dll. Di tempat lain, asalkan fungsi didokumentasikan dengan baik, it's pemanggil's tanggung jawab untuk lulus tepat argumen.

 wim
wim
Jawaban edit 6 September 2016 в 4:45
64
0
 rlms
rlms
30 Oktober 2013 в 2:24
2013-10-30T14:24:28+00:00
Lebih
Sumber
Sunting
#22666969

Edit: karena 2019 ada lebih banyak dukungan untuk menggunakan jenis penjelasan dan statis memeriksa di Python; check out mengetik modul dan mypy. 2013 jawaban berikut:


Jenis pemeriksaan ini umumnya tidak Pythonic. Di Python, itu lebih biasa digunakan duck typing. Contoh:

Dalam kode anda, menganggap bahwa argumen (dalam contoh a) berjalan seperti sebuah int, dan bersuara seperti sebuah int. Misalnya:

def my_function(a):
    return a + 7

Ini berarti bahwa tidak hanya anda fungsi bekerja dengan bilangan bulat, hal ini juga bekerja dengan mengapung dan setiap pengguna kelas didefinisikan dengan __add__ metode didefinisikan, sehingga lebih sedikit (kadang-kadang tidak ada) harus dilakukan jika anda, atau orang lain, ingin memperluas fungsi untuk bekerja dengan sesuatu yang lain. Namun, dalam beberapa kasus, anda mungkin perlu aplikasi int, maka anda bisa melakukan sesuatu seperti ini:

def my_function(a):
    b = int(a) + 7
    c = (5, 6, 3, 123541)[b]
    return c

dan fungsi masih bekerja untuk setiap a yang mendefinisikan __int__ metode.

Dalam menjawab pertanyaan anda yang lain, saya pikir itu adalah yang terbaik (sebagai jawaban yang lain mengatakan untuk melakukan hal ini:

def my_function(a, b, c):
    assert 0 < b < 10
    assert c        # A non-empty string has the Boolean value True

atau

def my_function(a, b, c):
    if 0 < b < 10:
        # Do stuff with b
    else:
        raise ValueError
    if c:
        # Do stuff with c
    else:
        raise ValueError

Beberapa jenis pemeriksaan dekorator yang saya buat:

import inspect

def checkargs(function):
    def _f(*arguments):
        for index, argument in enumerate(inspect.getfullargspec(function)[0]):
            if not isinstance(arguments[index], function.__annotations__[argument]):
                raise TypeError("{} is not of type {}".format(arguments[index], function.__annotations__[argument]))
        return function(*arguments)
    _f.__doc__ = function.__doc__
    return _f

def coerceargs(function):
    def _f(*arguments):
        new_arguments = []
        for index, argument in enumerate(inspect.getfullargspec(function)[0]):
            new_arguments.append(function.__annotations__[argument](arguments[index]))
        return function(*new_arguments)
    _f.__doc__ = function.__doc__
    return _f

if __name__ == "__main__":
    @checkargs
    def f(x: int, y: int):
        """
        A doc string!
        """
        return x, y

    @coerceargs
    def g(a: int, b: int):
        """
        Another doc string!
        """
        return a + b

    print(f(1, 2))
    try:
        print(f(3, 4.0))
    except TypeError as e:
        print(e)

    print(g(1, 2))
    print(g(3, 4.0))
 rlms
rlms
Jawaban edit 28 Juli 2019 в 2:47
20
0
Matthew Plourde
Matthew Plourde
30 Oktober 2013 в 2:06
2013-10-30T14:06:11+00:00
Lebih
Sumber
Sunting
#22666961

Salah satu cara adalah dengan menggunakan menegaskan:

def myFunction(a,b,c):
    "This is an example function I'd like to check arguments of"
    assert isinstance(a, int), 'a should be an int'
    # or if you want to allow whole number floats: assert int(a) == a
    assert b > 0 and b < 10, 'b should be betwen 0 and 10'
    assert isinstance(c, str) and c, 'c should be a non-empty string'
Matthew Plourde
Matthew Plourde
Jawaban edit 30 Oktober 2013 в 5:48
11
0
 DominikStyp
DominikStyp
25 Maret 2014 в 3:08
2014-03-25T03:08:45+00:00
Lebih
Sumber
Sunting
#22666971

Anda dapat menggunakan Jenis Penegak menerima/kembali dekorator dari PythonDecoratorLibrary It's sangat mudah dan dapat dibaca:

@accepts(int, int, float)
def myfunc(i1, i2, i3):
    pass
6
0
Games Brainiac
Games Brainiac
30 Oktober 2013 в 2:26
2013-10-30T14:26:52+00:00
Lebih
Sumber
Sunting
#22666970

Ada berbagai cara untuk memeriksa apa itu variabel dalam Python. Jadi, untuk daftar beberapa:

  • isinstance(obj, jenis) fungsi mengambil variabel anda, obj dan memberikan Benar itu adalah jenis yang sama dari jenis anda terdaftar.

  • issubclass(obj, kelas) fungsi yang diperlukan dalam suatu variabel obj, dan memberi anda Benar jika obj adalah sebuah subclass dari kelas. Jadi misalnya issubclass(Kelinci, Hewan) akan memberikan anda sebuah Kenyataan nilai

  • hasattr adalah contoh lain, ditunjukkan oleh fungsi ini, super_len:


def super_len(o):
    if hasattr(o, '__len__'):
        return len(o)

    if hasattr(o, 'len'):
        return o.len

    if hasattr(o, 'fileno'):
        try:
            fileno = o.fileno()
        except io.UnsupportedOperation:
            pass
        else:
            return os.fstat(fileno).st_size

    if hasattr(o, 'getvalue'):
        # e.g. BytesIO, cStringIO.StringI
        return len(o.getvalue())

hasattr bersandar lebih ke arah bebek-mengetik, dan sesuatu yang biasanya lebih pythonic tapi istilah itu adalah dogmatis.

Sebagai catatan, menegaskan pernyataan yang biasanya digunakan dalam pengujian, jika tidak, hanya menggunakan `if/else pernyataan.

5
0
 smarie
smarie
21 Desember 2017 в 2:52
2017-12-21T14:52:34+00:00
Lebih
Sumber
Sunting
#22666975

Saya melakukan sedikit investigasi pada topik yang baru-baru ini karena saya tidak puas dengan banyak perpustakaan yang saya temukan di luar sana.

Saya akhirnya mengembangkan perpustakaan untuk mengatasi hal ini, bernama valid8. Seperti yang dijelaskan dalam dokumentasi, hal ini untuk nilai validasi sebagian besar (meskipun itu datang dibundel dengan sederhana tipe validasi fungsi juga), dan anda mungkin ingin mengasosiasikan dengan PEP484-berdasarkan jenis checker seperti menegakkan atau pytypes.

Ini adalah cara anda akan melakukan validasi dengan valid8 sendirian (dan mini_lambda sebenarnya, untuk menentukan validasi logika - tapi itu tidak wajib) dalam kasus anda:

# for type validation
from numbers import Integral
from valid8 import instance_of

# for value validation
from valid8 import validate_arg
from mini_lambda import x, s, Len

@validate_arg('a', instance_of(Integral))
@validate_arg('b', (0 < x) & (x < 10))
@validate_arg('c', instance_of(str), Len(s) > 0)
def my_function(a: Integral, b, c: str):
    """an example function I'd like to check the arguments of."""
    # check that a is an int
    # check that 0 < b < 10
    # check that c is not an empty string

# check that it works
my_function(0.2, 1, 'r')  # InputValidationError for 'a' HasWrongType: Value should be an instance of <class 'numbers.Integral'>. Wrong value: [0.2].
my_function(0, 0, 'r')    # InputValidationError for 'b' [(x > 0) & (x < 10)] returned [False]
my_function(0, 1, 0)      # InputValidationError for 'c' Successes: [] / Failures: {"instance_of_<class 'str'>": "HasWrongType: Value should be an instance of <class 'str'>. Wrong value: [0]", 'len(s) > 0': "TypeError: object of type 'int' has no len()"}.
my_function(0, 1, '')     # InputValidationError for 'c' Successes: ["instance_of_<class 'str'>"] / Failures: {'len(s) > 0': 'False'}

Dan ini adalah contoh yang sama memanfaatkan PEP484 jenis petunjuk dan mendelegasikan jenis pemeriksaan untuk menegakkan:

# for type validation
from numbers import Integral
from enforce import runtime_validation, config
config(dict(mode='covariant'))  # type validation will accept subclasses too

# for value validation
from valid8 import validate_arg
from mini_lambda import x, s, Len

@runtime_validation
@validate_arg('b', (0 < x) & (x < 10))
@validate_arg('c', Len(s) > 0)
def my_function(a: Integral, b, c: str):
    """an example function I'd like to check the arguments of."""
    # check that a is an int
    # check that 0 < b < 10
    # check that c is not an empty string

# check that it works
my_function(0.2, 1, 'r')  # RuntimeTypeError 'a' was not of type <class 'numbers.Integral'>
my_function(0, 0, 'r')    # InputValidationError for 'b' [(x > 0) & (x < 10)] returned [False]
my_function(0, 1, 0)      # RuntimeTypeError 'c' was not of type <class 'str'>
my_function(0, 1, '')     # InputValidationError for 'c' [len(s) > 0] returned [False].
 smarie
smarie
Jawaban edit 21 Desember 2017 в 3:19
2
0
Mats Kindahl
Mats Kindahl
30 Oktober 2013 в 2:07
2013-10-30T14:07:53+00:00
Lebih
Sumber
Sunting
#22666963

Biasanya, anda melakukan sesuatu seperti ini:

def myFunction(a,b,c):
   if not isinstance(a, int):
      raise TypeError("Expected int, got %s" % (type(a),))
   if b <= 0 or b >= 10:
      raise ValueError("Value %d out of range" % (b,))
   if not c:
      raise ValueError("String was empty")

   # Rest of function
2
0
Mahdi Ghelichi
Mahdi Ghelichi
19 September 2017 в 10:07
2017-09-19T22:07:15+00:00
Lebih
Sumber
Sunting
#22666973

Ini memeriksa jenis input argumen pada saat pemanggilan fungsi:

def func(inp1:int=0,inp2:str="*"):

    for item in func.__annotations__.keys():
        assert isinstance(locals()[item],func.__annotations__[item])

    return (something)

first=7
second="$"
print(func(first,second))

Juga periksa dengan kedua=9` (itu harus memberikan pernyataan kesalahan)

Mahdi Ghelichi
Mahdi Ghelichi
Jawaban edit 20 September 2017 в 1:41
1
0
 FallenAngel
FallenAngel
30 Oktober 2013 в 2:08
2013-10-30T14:08:52+00:00
Lebih
Sumber
Sunting
#22666965
def someFunc(a, b, c):
    params = locals()
    for _item in params:
        print type(params[_item]), _item, params[_item]

Demo:

>> someFunc(1, 'asd', 1.0)
>> <type 'int'> a 1
>> <type 'float'> c 1.0
>> <type 'str'> b asd

lebih lanjut tentang setempat()

0
0
Paulo Bu
Paulo Bu
30 Oktober 2013 в 2:11
2013-10-30T14:11:07+00:00
Lebih
Sumber
Sunting
#22666967

Jika anda ingin melakukan validasi untuk beberapa fungsi yang dapat anda tambahkan logika dalam dekorator seperti ini:

def deco(func):
     def wrapper(a,b,c):
         if not isinstance(a, int)\
            or not isinstance(b, int)\
            or not isinstance(c, str):
             raise TypeError
         if not 0 < b < 10:
             raise ValueError
         if c == '':
             raise ValueError
         return func(a,b,c)
     return wrapper

dan menggunakannya:

@deco
def foo(a,b,c):
    print 'ok!'

Harap ini membantu!

0
0
Jo So
Jo So
30 Oktober 2013 в 2:06
2013-10-30T14:06:39+00:00
Lebih
Sumber
Sunting
#22666962

Jika anda ingin memeriksa **kwargs, *args serta normal argumen dalam satu pergi, anda dapat menggunakan setempat() fungsi sebagai pernyataan pertama dalam definisi fungsi untuk mendapatkan kamus argumen.

Kemudian menggunakan jenis() untuk memeriksa argumen, misalnya sementara iterasi dict.

def myfunc(my, args, to, this, function, **kwargs):
    d = locals()
    assert(type(d.get('x')) == str)
    for x in d:
        if x != 'x':
            assert(type(d[x]) == x
    for x in ['a','b','c']:
        assert(x in d)

    whatever more...
0
0
Mohit Thakur
Mohit Thakur
25 September 2017 в 9:29
2017-09-25T09:29:16+00:00
Lebih
Sumber
Sunting
#22666974

Ini bukan solusi untuk anda, tetapi jika anda ingin membatasi fungsi panggilan untuk beberapa jenis parameter tertentu maka anda harus menggunakan PROATOR { Python prototipe Fungsi validator }. anda bisa lihat link berikut. https://github.com/mohit-thakur-721/proator

0
0
Nicolas Brugneaux
Nicolas Brugneaux
30 Oktober 2013 в 2:08
2013-10-30T14:08:56+00:00
Lebih
Sumber
Sunting
#22666966
def myFunction(a,b,c):
"This is an example function I'd like to check arguments of"
    if type( a ) == int:
       #dostuff
    if 0 < b < 10:
       #dostuff
    if type( C ) == str and c != "":
       #dostuff
-1
0
Related communities 6
Python Indonesia
Python Indonesia
24 686 pengguna
Programmer Python Indonesia. Group ini dikelola oleh sejumlah admin. Baca pesan tersemat / pinned message: https://t.me/pythonID/217588
Buka telegram
Indonesian Python Warriors
Indonesian Python Warriors
1 462 pengguna
Di grup ini tidak ada kewajiban untuk menggunakan username dan foto. Yang tidak boleh adalah spamming. Gak boleh baper, kalau nanya yang bener, eror jangan difoto pake HP, gunakan screenshot, code copas ke pastebin.com lalu share link ke sini.
Buka telegram
Python-ID Jogja
Python-ID Jogja
962 pengguna
Buka telegram
BASIC PYTHON INDONESIA
BASIC PYTHON INDONESIA
214 pengguna
Buka telegram
Python Newbie Indonesia
Python Newbie Indonesia
180 pengguna
Peraturan grup Python Newbie 🌻Dilarang spam 🌻Dilarang menggunakan bahasa kasar 🌻Dilarang beriklan di grup tanpa seizin admin Beberapa video dasar python dapat diliat melalui channel youtube juan aditya Jangan lupa untuk subscribe ya🙏
Buka telegram
PythonWealth Indonesia 🇮🇩
PythonWealth Indonesia 🇮🇩
29 pengguna
Buka telegram
Tambahkan pertanyaan
Kategori
Semua
Teknologi
Budaya / Rekreasi
Kehidupan / Seni
Ilmu Pengetahuan
Profesional
Bisnis
Pengguna
Semua
Baru
Populer
1
Asilbek Qadamboyev
Terdaftar 6 jam yang lalu
2
Akshit Mehta
Terdaftar 2 hari yang lalu
3
me you
Terdaftar 5 hari yang lalu
4
Никита иванов
Terdaftar 1 minggu yang lalu
5
Alex1976G_06
Terdaftar 1 minggu yang lalu
ID
JA
KO
RU
© de-vraag 2022
Sumber
stackoverflow.com
di bawah lisensi cc by-sa 3.0 dengan atribusi