René's URL Explorer Experiment


Title: Class Internals (Video) – Real Python

Open Graph Title: Class Internals – Real Python

Description: In the previous lesson, I introduced you to multiple inheritance. In this lesson, I’ll show you different bits about class internals and how you can use them to make more powerful code. In part one of this multi-part course, I showed you how the…

Open Graph Description: In the previous lesson, I introduced you to multiple inheritance. In this lesson, I’ll show you different bits about class internals and how you can use them to make more powerful code. In part one of this multi-part course, I showed you how the…

Opengraph URL: https://realpython.com/lessons/python-class-internals/

X: @realpython

direct link

Domain: realpython.com


Hey, it has json ld scripts:
  {
    "@context": "https://schema.org",
    "@type": "VideoObject",
    "name": "Class Internals",
    "description": "In the previous lesson, I introduced you to multiple inheritance. In this lesson, I’ll show you different bits about class internals and how you can use them to make more powerful code. In part one of this multi\u002Dpart course, I showed you how the…",
    "thumbnailUrl": ["https://files.realpython.com/media/Inheritance-and-Internals-Object-Oriented-Programming-in-Python_Watermarked.6481cc1cbbb5.jpg"],
    "uploadDate": "2023-09-12T14:00:00+00:00",
    "duration": "PT8M58S",
    
    "potentialAction": {
      "@type": "SeekToAction",
      "target": "https://realpython.com/lessons/python-class-internals/#t={seek_to_second_number}",
      "startOffset-input": "required name=seek_to_second_number"
    }
  }
  

authorReal Python
twitter:cardsummary_large_image
twitter:imagehttps://files.realpython.com/media/Inheritance-and-Internals-Object-Oriented-Programming-in-Python_Watermarked.6481cc1cbbb5.jpg
og:imagehttps://files.realpython.com/media/Inheritance-and-Internals-Object-Oriented-Programming-in-Python_Watermarked.6481cc1cbbb5.jpg
twitter:creator@realpython
og:typevideo.episode

Links:

https://realpython.com/
Start Herehttps://realpython.com/start-here/
Learn Python https://realpython.com/lessons/python-class-internals/
Python Tutorials →In-depth articles and video courseshttps://realpython.com/search?kind=article&kind=course&order=newest
Learning Paths →Guided study plans for accelerated learninghttps://realpython.com/learning-paths/
Quizzes & Exercises →Check your learning progresshttps://realpython.com/quizzes/
Browse Topics →Focus on a specific area or skill levelhttps://realpython.com/tutorials/all/
Community Chat →Learn with other Pythonistashttps://realpython.com/community/
Office Hours →Live Q&A calls with Python expertshttps://realpython.com/office-hours/
Podcast →Hear what’s new in the world of Pythonhttps://realpython.com/podcasts/rpp/
Books →Round out your knowledge and learn offlinehttps://realpython.com/products/books/
Reference →Concise definitions for common Python termshttps://realpython.com/ref/
Code Mentor →BetaPersonalized code assistance & learning toolshttps://realpython.com/mentor/
Unlock All Content →https://realpython.com/account/join/
More https://realpython.com/lessons/python-class-internals/
Learner Storieshttps://realpython.com/learner-stories/
Python Newsletterhttps://realpython.com/newsletter/
Python Job Boardhttps://www.pythonjobshq.com
Meet the Teamhttps://realpython.com/team/
Become a Tutorial Writerhttps://realpython.com/write-for-us/
Become a Video Instructorhttps://realpython.com/become-an-instructor/
Searchhttps://realpython.com/search
https://realpython.com/search
Joinhttps://realpython.com/account/join/
Sign‑Inhttps://realpython.com/account/login/?next=%2Flessons%2Fpython-class-internals%2F
Unlock This Lessonhttps://realpython.com/account/join/?utm_source=rp_lesson&utm_content=python-class-inheritance
Unlock This Lessonhttps://realpython.com/account/join/?utm_source=rp_lesson&utm_content=python-class-inheritance
https://realpython.com/courses/python-class-inheritance/#team
Inheritance and Internals: Object-Oriented Programming in Pythonhttps://realpython.com/courses/python-class-inheritance/
Christopher Trudeauhttps://realpython.com/courses/python-class-inheritance/#team
Recommended Tutorialhttps://realpython.com/python-classes/
Course Slides (.pdf)https://realpython.com/courses/python-class-inheritance/downloads/python-class-inheritance-slides/
Sample Code (.zip)https://realpython.com/courses/python-class-inheritance/downloads/python-class-inheritance-code/
Ask a Questionhttps://realpython.com/lessons/python-class-internals/#discussion
https://realpython.com/feedback/survey/course/python-class-inheritance/liked/?from=lesson-title
https://realpython.com/feedback/survey/course/python-class-inheritance/disliked/?from=lesson-title
Transcripthttps://realpython.com/lessons/python-class-internals/#transcript
Discussion (6)https://realpython.com/lessons/python-class-internals/#discussion
00:00https://realpython.com/lessons/python-class-internals/#t=0.57
In the previous lesson, I introduced you to multiple inheritance.https://realpython.com/lessons/python-class-internals/#t=0.57
In this lesson,https://realpython.com/lessons/python-class-internals/#t=4.17
I’ll show you different bits about class internals and how you can use them tohttps://realpython.com/lessons/python-class-internals/#t=4.9399999999999995
make more powerful code. In part one of this multi-part course,https://realpython.com/lessons/python-class-internals/#t=8.39
I showed you how the @property and @.setter decorators work to let you write codehttps://realpython.com/lessons/python-class-internals/#t=13.35
that makes methods behave like attributes.https://realpython.com/lessons/python-class-internals/#t=17.03
00:20https://realpython.com/lessons/python-class-internals/#t=20.41
This is part of something called the descriptor protocol.https://realpython.com/lessons/python-class-internals/#t=20.41
A protocol in Python is a loose collection of functions—https://realpython.com/lessons/python-class-internals/#t=24.34
or in this case, methods—that act as a promise.https://realpython.com/lessons/python-class-internals/#t=27.28
00:30https://realpython.com/lessons/python-class-internals/#t=30.84
If you implement their interface, Python will give you some functionality.https://realpython.com/lessons/python-class-internals/#t=30.84
Many things that seem like magic are actually implemented as protocolshttps://realpython.com/lessons/python-class-internals/#t=35.17
underneath, meaning you can write your own classes that behave the same way.https://realpython.com/lessons/python-class-internals/#t=39.02
00:43https://realpython.com/lessons/python-class-internals/#t=43.24
For example, both iteration and sequence functionality are built on protocols.https://realpython.com/lessons/python-class-internals/#t=43.24
The descriptor protocol is the one that makes methods act like attributes.https://realpython.com/lessons/python-class-internals/#t=48.4
00:53https://realpython.com/lessons/python-class-internals/#t=53.29
The @property and @.setter decorators are the simpler version of the descriptorhttps://realpython.com/lessons/python-class-internals/#t=53.29
protocol. They abstract away a deeper mechanism that uses dunder methods.https://realpython.com/lessons/python-class-internals/#t=57.79
01:02https://realpython.com/lessons/python-class-internals/#t=62.97
.__set_name__(), .__get__(), and .set() are all used to implement the same features thosehttps://realpython.com/lessons/python-class-internals/#t=62.97
decorators do, and you can override these methods and define your own behavior.https://realpython.com/lessons/python-class-internals/#t=68.15
01:13https://realpython.com/lessons/python-class-internals/#t=73.55
Lets go look at an example.https://realpython.com/lessons/python-class-internals/#t=73.55
01:17https://realpython.com/lessons/python-class-internals/#t=77.35
If you ever needed an integer that had a more positive outlook on life,https://realpython.com/lessons/python-class-internals/#t=77.35
well, let’s build one. One that doesn’t allow negative values.https://realpython.com/lessons/python-class-internals/#t=81.28
They clog up your aura.https://realpython.com/lessons/python-class-internals/#t=85.2
01:27https://realpython.com/lessons/python-class-internals/#t=87.63
You can write a class that abstracts a positive integer using the descriptorhttps://realpython.com/lessons/python-class-internals/#t=87.63
protocol. First off,https://realpython.com/lessons/python-class-internals/#t=91.61
.__set_name__() is called when a descriptor is instantiated, and it’s passedhttps://realpython.com/lessons/python-class-internals/#t=93.23
the name of the variable it is being assigned to. For example,https://realpython.com/lessons/python-class-internals/#t=98.19
if you read a line of code that says radius = PositiveInteger,https://realpython.com/lessons/python-class-internals/#t=101.91
instantiating a PositiveInteger object and storing it in a reference calledhttps://realpython.com/lessons/python-class-internals/#t=105.88
radius, the name radius gets passed into .__set_name__().https://realpython.com/lessons/python-class-internals/#t=109.75
01:54https://realpython.com/lessons/python-class-internals/#t=114.62
What you typically do with that value is store it for later use.https://realpython.com/lessons/python-class-internals/#t=114.62
I’m sticking it in ._name, logically enough.https://realpython.com/lessons/python-class-internals/#t=118.73
02:03https://realpython.com/lessons/python-class-internals/#t=123.21
The core of what you do with the descriptor protocol is get and set values.https://realpython.com/lessons/python-class-internals/#t=123.21
.__get__() is the get part.https://realpython.com/lessons/python-class-internals/#t=127.77
This method is past the object that the descriptor is attached to and the classhttps://realpython.com/lessons/python-class-internals/#t=130.22
of the descriptor itself.https://realpython.com/lessons/python-class-internals/#t=134.41
02:16https://realpython.com/lessons/python-class-internals/#t=136.53
That’d be the radius and the Circle class in the example I just mentioned.https://realpython.com/lessons/python-class-internals/#t=136.53
I’m not really sure why they bother passing in the class as you can always gethttps://realpython.com/lessons/python-class-internals/#t=142.09
it through self.__class__, but it’s part of the protocol,https://realpython.com/lessons/python-class-internals/#t=146.39
so it has to be in the signature.https://realpython.com/lessons/python-class-internals/#t=149.89
02:32https://realpython.com/lessons/python-class-internals/#t=152.9
All the getter for PositiveInteger needs to do is return the associated value.https://realpython.com/lessons/python-class-internals/#t=152.9
The value is on the instance object under the name associated with thishttps://realpython.com/lessons/python-class-internals/#t=158.06
descriptor.https://realpython.com/lessons/python-class-internals/#t=162.32
02:43https://realpython.com/lessons/python-class-internals/#t=163.62
.__dict__ is the dictionary associated with all Python objects wherehttps://realpython.com/lessons/python-class-internals/#t=163.62
Python stores the objects attributes.https://realpython.com/lessons/python-class-internals/#t=168.22
It feels like there’s a lot going on in this line,https://realpython.com/lessons/python-class-internals/#t=171.7
but all it’s doing is getting the value from the associated instance object.https://realpython.com/lessons/python-class-internals/#t=174.22
02:59https://realpython.com/lessons/python-class-internals/#t=179.47
There are situations where this won’t work,https://realpython.com/lessons/python-class-internals/#t=179.47
but I’ll come back to that edge case in a later lesson. All right,https://realpython.com/lessons/python-class-internals/#t=181.68
you’ve got the getter. Now the setter.https://realpython.com/lessons/python-class-internals/#t=185.88
This signature takes the instance object associated with the value and the newhttps://realpython.com/lessons/python-class-internals/#t=188.87
value to set it to. The purpose of PositiveInteger is to makehttps://realpython.com/lessons/python-class-internals/#t=192.76
sure that it can only store positive integer. This line enforces that.https://realpython.com/lessons/python-class-internals/#t=197.67
03:23https://realpython.com/lessons/python-class-internals/#t=203.9
Once the error checking’s out of the way,https://realpython.com/lessons/python-class-internals/#t=203.9
now I need to actually assign the value.https://realpython.com/lessons/python-class-internals/#t=205.89
This uses the same .__dict__ mechanism as the getter,https://realpython.com/lessons/python-class-internals/#t=208.5
but this time assigning the value to that dictionary.https://realpython.com/lessons/python-class-internals/#t=211.73
03:36https://realpython.com/lessons/python-class-internals/#t=216.12
Let me scroll down, and you’ll see how this gets used.https://realpython.com/lessons/python-class-internals/#t=216.12
03:40https://realpython.com/lessons/python-class-internals/#t=220.52
Circles are, so yesterday. Let’s define an Ellipse.https://realpython.com/lessons/python-class-internals/#t=220.52
The ellipse has two radius-like things: a width and a height. Mathematically,https://realpython.com/lessons/python-class-internals/#t=224.47
there are a half dozen ways to specify an ellipse,https://realpython.com/lessons/python-class-internals/#t=229.14
but width and height are commonly used in drawing libraries,https://realpython.com/lessons/python-class-internals/#t=231.69
so let’s stick with that. Instead of specifying focal points,https://realpython.com/lessons/python-class-internals/#t=234.27
I want both my width and my height to be positive integers,https://realpython.com/lessons/python-class-internals/#t=238.6
so I use our newly minted class.https://realpython.com/lessons/python-class-internals/#t=241.98
04:05https://realpython.com/lessons/python-class-internals/#t=245.39
This is a little tricky, .width and .height here are class attributes,https://realpython.com/lessons/python-class-internals/#t=245.39
which means the instantiation of PositiveInteger is associated with thehttps://realpython.com/lessons/python-class-internals/#t=250.77
Ellipse’s class, not the objects.https://realpython.com/lessons/python-class-internals/#t=255.28
04:18https://realpython.com/lessons/python-class-internals/#t=258.95
This is how you register a descriptor. Inside Ellipse’s .__init__(),https://realpython.com/lessons/python-class-internals/#t=258.95
When I go to assign a value, the descriptor on the class is getting invoked,https://realpython.com/lessons/python-class-internals/#t=264.02
but because the descriptor’s code actually stores the contents on the object,https://realpython.com/lessons/python-class-internals/#t=268.77
the end result is a value associated with the object, not the class.https://realpython.com/lessons/python-class-internals/#t=272.61
04:37https://realpython.com/lessons/python-class-internals/#t=277.81
My brain hurts a little when I think about this.https://realpython.com/lessons/python-class-internals/#t=277.81
You don’t really need to understand this. When you use descriptors,https://realpython.com/lessons/python-class-internals/#t=280.97
you can treat it like magic.https://realpython.com/lessons/python-class-internals/#t=283.65
If you build a descriptor and assign it in the class,https://realpython.com/lessons/python-class-internals/#t=285.33
the end result is stored values with the same name on the object.https://realpython.com/lessons/python-class-internals/#t=287.45
04:50https://realpython.com/lessons/python-class-internals/#t=290.85
It isn’t magic though. There’s no extra special stuff going on here.https://realpython.com/lessons/python-class-internals/#t=290.85
From the interpreter’s point of view,https://realpython.com/lessons/python-class-internals/#t=294.27
it’s a class attribute that references a particular kind of instance object,https://realpython.com/lessons/python-class-internals/#t=296.38
which implements the descriptor,https://realpython.com/lessons/python-class-internals/#t=301.16
which when assigned to passes the value through the descriptor protocol tohttps://realpython.com/lessons/python-class-internals/#t=303.24
the owning instance object. Does it spoil the magic to know how the trick works?https://realpython.com/lessons/python-class-internals/#t=308.09
05:13https://realpython.com/lessons/python-class-internals/#t=313.9
Alright, magic or not, let’s see it in practice. Importing …https://realpython.com/lessons/python-class-internals/#t=313.9
05:21https://realpython.com/lessons/python-class-internals/#t=321.03
and I’ll create an ellipse.https://realpython.com/lessons/python-class-internals/#t=321.03
05:25https://realpython.com/lessons/python-class-internals/#t=325.83
I’m gonna scroll the top window back up so that you can see the descriptorhttps://realpython.com/lessons/python-class-internals/#t=325.83
class. Since I put print statements in the descriptor,https://realpython.com/lessons/python-class-internals/#t=329.01
you can sort of see what’s going on.https://realpython.com/lessons/python-class-internals/#t=332.44
05:35https://realpython.com/lessons/python-class-internals/#t=335.12
The .__init__() for Ellipse assigns .width and .height.https://realpython.com/lessons/python-class-internals/#t=335.12
That assignment triggers .__set_name__() for PositiveInteger because it’shttps://realpython.com/lessons/python-class-internals/#t=338.81
registered as a descriptor, as a class attribute. This gets called twice,https://realpython.com/lessons/python-class-internals/#t=343.54
once for .width and once for .height. Now, when I access the .width attribute,https://realpython.com/lessons/python-class-internals/#t=348.37
05:55https://realpython.com/lessons/python-class-internals/#t=355.5
the print() in the getter is issued and returns the value.https://realpython.com/lessons/python-class-internals/#t=355.5
06:01https://realpython.com/lessons/python-class-internals/#t=361.94
Same goes for .height, and if I try to assign .height,https://realpython.com/lessons/python-class-internals/#t=361.94
06:08https://realpython.com/lessons/python-class-internals/#t=368.9
I get the error checking in the setter enforcing our positive outlook.https://realpython.com/lessons/python-class-internals/#t=368.9
No negativity for you.https://realpython.com/lessons/python-class-internals/#t=373.52
06:18https://realpython.com/lessons/python-class-internals/#t=378.56
Dictionaries aren’t free. They take up more memory than just their contents.https://realpython.com/lessons/python-class-internals/#t=378.56
The attributes on an object are stored as a dictionary. By default,https://realpython.com/lessons/python-class-internals/#t=383.58
That dictionary is named .__dict__.https://realpython.com/lessons/python-class-internals/#t=387.87
06:31https://realpython.com/lessons/python-class-internals/#t=391.07
If you don’t want the overhead cost of a dictionary associated with your class,https://realpython.com/lessons/python-class-internals/#t=391.07
you can slim it down using the special attribute .__slots__.https://realpython.com/lessons/python-class-internals/#t=395.31
06:40https://realpython.com/lessons/python-class-internals/#t=400.07
What you put in .__slots__ is a tuple with the names of the attributes yourhttps://realpython.com/lessons/python-class-internals/#t=400.07
class uses. Technically, it can be a list instead of a tuple,https://realpython.com/lessons/python-class-internals/#t=404.4
but that has extra overhead as well,https://realpython.com/lessons/python-class-internals/#t=408.38
and you’re never going to edit the contents. When .__slots__ is present,https://realpython.com/lessons/python-class-internals/#t=410.18
the underlying .__dict__ gets removed.https://realpython.com/lessons/python-class-internals/#t=415.34
06:58https://realpython.com/lessons/python-class-internals/#t=418.46
If you attempt to use .__dict__,https://realpython.com/lessons/python-class-internals/#t=418.46
you’ll get an AttributeError because it isn’t there, and of course,https://realpython.com/lessons/python-class-internals/#t=420.38
without .__dict__, there is nowhere to put new attributes.https://realpython.com/lessons/python-class-internals/#t=424.62
07:08https://realpython.com/lessons/python-class-internals/#t=428.48
Attempting to add an attribute that isn’t in .__slots__ will also result inhttps://realpython.com/lessons/python-class-internals/#t=428.48
an error.https://realpython.com/lessons/python-class-internals/#t=433.35
07:16https://realpython.com/lessons/python-class-internals/#t=436.16
Consider this class containing an "x" and a "y" coordinate. By putting "x" andhttps://realpython.com/lessons/python-class-internals/#t=436.16
"y" in .__slots__, I get rid of the underlying .__dict__.https://realpython.com/lessons/python-class-internals/#t=441.2
07:25https://realpython.com/lessons/python-class-internals/#t=445.83
This saves a bit of memory, a little over 400 bytes per object.https://realpython.com/lessons/python-class-internals/#t=445.83
Remember earlier when I said I’ll save that for later in the lesson? Well,https://realpython.com/lessons/python-class-internals/#t=450.89
it’s later in the lesson.https://realpython.com/lessons/python-class-internals/#t=454.44
07:36https://realpython.com/lessons/python-class-internals/#t=456.19
The PositiveInteger descriptor class worked by assuming the instance object ithttps://realpython.com/lessons/python-class-internals/#t=456.19
was associated with had a .__dict__. If you use .__slots__,https://realpython.com/lessons/python-class-internals/#t=460.97
that assumption isn’t valid.https://realpython.com/lessons/python-class-internals/#t=465.8
07:48https://realpython.com/lessons/python-class-internals/#t=468.41
I played around with this for a while trying to figure out if I could get thehttps://realpython.com/lessons/python-class-internals/#t=468.41
two things to work together.https://realpython.com/lessons/python-class-internals/#t=471.57
I found some code on the internet that showed a way to make it work,https://realpython.com/lessons/python-class-internals/#t=473.13
but it had two problems.https://realpython.com/lessons/python-class-internals/#t=475.67
07:57https://realpython.com/lessons/python-class-internals/#t=477.28
Problem one is it resulted in a new .__dict__ being created,https://realpython.com/lessons/python-class-internals/#t=477.28
which defeats the purpose of .__slots__,https://realpython.com/lessons/python-class-internals/#t=481.02
and problem two was I couldn’t get it to work in Python 3.https://realpython.com/lessons/python-class-internals/#t=483.27
08:06https://realpython.com/lessons/python-class-internals/#t=486.71
Python 3 seems to be a bit more aggressive about the constraint. In theory,https://realpython.com/lessons/python-class-internals/#t=486.71
you could write a descriptor class that stored values in the descriptor classhttps://realpython.com/lessons/python-class-internals/#t=491.79
instead of on the instance object. As there can be multiple instance objects,https://realpython.com/lessons/python-class-internals/#t=495.32
you’d need to store that value in a nested dictionary with the top level keyed onhttps://realpython.com/lessons/python-class-internals/#t=500.39
the instance and the second level storing the values for that instance.https://realpython.com/lessons/python-class-internals/#t=504.85
08:29https://realpython.com/lessons/python-class-internals/#t=509.01
I haven’t tried this,https://realpython.com/lessons/python-class-internals/#t=509.01
but there’s no reason it wouldn’t work that I can think of.https://realpython.com/lessons/python-class-internals/#t=510.43
But if you get what I’m laying down, the short version of it is, I just said,https://realpython.com/lessons/python-class-internals/#t=513.03
reimplement .__dict__, but somewhere else.https://realpython.com/lessons/python-class-internals/#t=518.13
08:40https://realpython.com/lessons/python-class-internals/#t=520.55
You’ll essentially be losing almost all the gains of .__slots__. Not quite all,https://realpython.com/lessons/python-class-internals/#t=520.55
but close enough. The short version is don’t mix descriptors and slots.https://realpython.com/lessons/python-class-internals/#t=524.65
08:49https://realpython.com/lessons/python-class-internals/#t=529.52
It’s a recipe for disaster.https://realpython.com/lessons/python-class-internals/#t=529.52
08:53https://realpython.com/lessons/python-class-internals/#t=533.19
I hope you’re not tired of dunder stuff. More to come in the next lesson.https://realpython.com/lessons/python-class-internals/#t=533.19
Dec. 9, 2023https://realpython.com/lessons/python-class-internals/#comment-519ad83d-a7ff-456c-85f0-cb8a5ffa77a1
Dec. 9, 2023https://realpython.com/lessons/python-class-internals/#comment-456b1116-191a-41ee-847e-7a0a612050b9
Dec. 9, 2023https://realpython.com/lessons/python-class-internals/#comment-2487849f-472b-40b0-afff-d46bd8337c9d
Dec. 11, 2023https://realpython.com/lessons/python-class-internals/#comment-44e695c8-bf7d-43aa-a52f-bfe37a4705a4
Dec. 11, 2023https://realpython.com/lessons/python-class-internals/#comment-8a5bcf78-b7a8-40a7-af14-ccfb89ec7d26
Dec. 11, 2023https://realpython.com/lessons/python-class-internals/#comment-4cb9a15f-08ca-4ab7-a001-9500a6781d93
Become a Memberhttps://realpython.com/account/join/
https://realpython.com/lessons/python-multiple-inheritance/
Overviewhttps://realpython.com/courses/python-class-inheritance/
https://realpython.com/lessons/python-class-special-methods/
Inheritance and Internals: OOP in Python (Overview) 03:38 https://realpython.com/videos/python-class-inheritance-overview/
Inheritance Inside Python 09:07 https://realpython.com/videos/inheritance-in-python/
Multiple Inheritance - Multiple Parents 09:30 https://realpython.com/lessons/python-multiple-inheritance/
Class Internals 08:58 https://realpython.com/lessons/python-class-internals/
More Special Methods 06:50 https://realpython.com/lessons/python-class-special-methods/
Classes in the Standard Library 07:23 https://realpython.com/lessons/python-class-standard-library/
Abstracts and Interfaces 08:41 https://realpython.com/lessons/python-class-abstracts-and-interfaces/
Inheritance and Internals: OOP in Python (Summary) 02:21 https://realpython.com/lessons/python-class-inheritance-summary/
Privacy Policyhttps://realpython.com/privacy-policy/

Viewport: width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover

Robots: max-image-preview:large


URLs of crawlers that visited me.