<p>A regular Python print statement might look like this (Dart printing looks similar to this):</p>
<pre class="lang-python prettyprint-override"><code>print(a_number) # for a number
print(a_string) # for a string
</code></pre>
<p>These print statements can accept arbitrary values without type specification.</p>
<p>In C using <code>printf</code>, a print statement might look like this:</p>
<pre class="lang-c prettyprint-override"><code>printf(&quot;%d&quot;, a_number); // for a number
printf(&quot;%s\n&quot;, a_string.c_str()); // for a string
</code></pre>
<p>This one <em>can't</em>, the programmer needs to specify the type.</p>
<p>What are the main differences between designing a print function similar to Python's <code>print()</code> as opposed to C's <code>printf()</code>?</p>

<language-design><functions><language-comparison><print>
What are the differences between designing a print function Python-style as opposed to C-style?
CC BY-SA 4.0
2023-06-25T01:38:20.083Z
Not my downvotes, but they could be caused by the inappropriate use of "C++" when what is really meant is "C", and by using a non-equivalent Python example instead of `print(f'{a_number}')`.
Ray Butterworth
CC BY-SA 4.0
2023-06-25T06:05:52.800Z
Changing `print(f'{x}')` from `print(x)` significantly changed the question. Those are two entirely separate printing modes
mousetail 'he-him'
CC BY-SA 4.0
2023-06-25T06:06:09.780Z
After the edit applying Ray's comment, the question has pivoted to more about string formatting than printing.
Michael Homer
CC BY-SA 4.0
2023-06-25T12:53:29.533Z
@MichaelHomer, it's not obvious exactly what the question *is* asking about. The two examples illustrate printing where the function determines the type of the arguments and where the caller specifies the type. Is that what the fundamental question is about, the ability to print an arbitrary object without having to specify its type? If so, the answer is that C doesn't do it because it isn't possible to do it. If not, what exactly *is* the characteristic that is meant by "C-style" and "Python-style"?
Ray Butterworth
CC BY-SA 4.0
2023-06-25T20:01:09.200Z
@RayButterworth Yes, there is [a meta post about this question](https://languagedesign.meta.stackexchange.com/questions/401/whats-gone-wrong-with-my-question), in case you haven’t seen it.
Michael Homer
CC BY-SA 4.0
2023-07-19T19:45:21.500Z
It's worth noting that C's printf became enough of a security and reliability concern that at least one modern C compiler has the ability to read and check printf format strings at compile time and also to warn about the common "printf(string)" misuse.
Peter Green
<p>It's mainly related to the basics of the type system, but sometimes related to other considerations on the philosophy of the language.</p>
<h3>print with arbitrary values</h3>
<p>Some languages have a <code>print</code> function that accepts arbitrary values. Others need the program to explicitly convert data to a string. What makes the difference is whether the language makes it possible to infer how to convert the data with some default conversion mechanism (e.g. integers printed in decimal, lists printed as something like the language's list constructor syntax, etc.).</p>
<p>In a dynamically typed language like Python, it's easy. All values have runtime type information, so the printing function can use that to determine how to print the argument.</p>
<p>In statically typed languages, there typically isn't enough information at runtime to find a sensible way to convert a value's representation to a string. For example, a word in memory could contain an integer or a float or a pointer and there's no way to find out which. So any automatic conversion to string has to follow some rules at compile time, where the compiler generates the correct formatting function based on the argument's static type.</p>
<p>C has no such mechanism. C is a low-level language which gives the programmer a lot of control. If you want to print an integer, you have to specify whether you want it in decimal, hexadecimal or octal. If you want to print a floating-point value, you have to specify how many digits of precision. This philosophy is prevalent in C, so the design of the language doesn't include any mechanism for selecting between different implementations of a function based on the argument type. (From C11 onwards there is such a mechanism, but it's restricted to selecting between floating point types for a few built-in functions.)</p>
<p>Many statically typed languages have an overloading mechanism that allows the selection of different implementations of a function based on the argument's type. For example, in C++, the <code>&lt;&lt;</code> operator selects between different printing functions based on the type of its right-hand argument, so you can write <code>cout &lt;&lt; 1</code>, <code>cout &lt;&lt; 1.5</code>, <code>cout &lt;&lt; &quot;hello&quot;</code>, etc. (And <code>&lt;&lt;</code> even selects between being a printing function and a bitwise operator, based on the type of its left-hand argument.) This is a compile-time mechanism: the compiler knows that there are many <code>&lt;&lt;</code> functions, each with their type. In Haskell, this is the <a href="https://hackage.haskell.org/package/base/docs/Text-Show.html" rel="nofollow noreferrer"><code>Show</code></a> <a href="https://en.wikipedia.org/wiki/Type_class" rel="nofollow noreferrer">type class</a>, with an instance for each type or type constructor for which a printing mechanism exists.</p>
<h3>printf with a template</h3>
<p>Almost all general-purpose languages have function that's similar to C's <code>sprintf</code>, often with a similar template syntax. They may or may not have a function that's similar to <code>printf</code>, combining the string templating with printing: when combining a string-printing function with a template formatting function is simple enough, there's no need for a function that does both.</p>
<p>For example, in Python, the equivalent of C's <code>sprintf</code> is the <a href="https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations" rel="nofollow noreferrer"><code>%</code> operator</a>, which uses a <a href="https://docs.python.org/3/library/stdtypes.html#old-string-formatting" rel="nofollow noreferrer">printf-like template syntax</a>. This templating mechanism <a href="http://www.nhn.ou.edu/%7Eatlas/d0/software/python1.5/lib/node11.html" rel="nofollow noreferrer">from the original version of the language</a> is somewhat deprecated in modern Python in favor of a <a href="https://docs.python.org/3/library/string.html#formatstrings" rel="nofollow noreferrer">different template syntax</a> that is accessible via the <a href="https://docs.python.org/3/library/stdtypes.html#str.format" rel="nofollow noreferrer"><code>format</code> method</a> on the template string; the syntax is different, but the core principle is the same. Python only has a <code>sprintf</code> equivalent, not a <code>printf</code> equivalent, because there wouldn't be much point: it's almost as easy to write <code>print(template % (arg1, arg2))</code> or <code>print(template.format(arg1, arg2))</code> as it would be to write <code>print(template, arg1, arg2)</code>.</p>
<p>Having separate functions requires constructing the resulting string in memory. In C, that's a big deal: you have to allocate enough memory, and C gives you a choice of allocators: you can use a global buffer, or a buffer on the stack, or a buffer allocated by <code>malloc</code>, or a buffer allocated by some custom allocator. So it's convenient to have a function that can directly print out the data piece by piece. In a high-level language like Python, the allocation is done entirely under the hood and allocating a string in memory is not considered a big deal.</p>
<p>The main reason some general-purpose don't have a <code>printf</code> or <code>sprintf</code>-like function is that it's hard to fit into a static type system. You have to match the content of the template string with the types of the other arguments, and even with the number of other arguments. In C, that's not a problem because the type system is very far from sound. It's the responsibility of the programmer to pass the correct number of arguments with the correct types. In dynamically typed languages, that's not a problem because the templating function can check argument types at runtime.</p>
<p>Many statically typed languages arrange some printf-like mechanism anyway, with various degree of “compiler magic”, i.e. you couldn't implement <code>printf</code> as a library function. For example, <a href="https://en.wikibooks.org/wiki/Pascal_Programming/Input_and_Output#Formatting_output" rel="nofollow noreferrer">Pascal</a> has special handling for some functions like <code>write</code> which accept multiple argument types and even a field width formatting syntax. You can write <code>write('x=', x:3)</code> but you can't make your own function that accepts a variable number of arguments, or that takes the extra width annotation <code>:3</code>. In Ocaml, the <a href="https://en.wikibooks.org/wiki/Pascal_Programming/Input_and_Output#Formatting_output" rel="nofollow noreferrer"><code>printf</code> mechanism</a> requires the template to be a string literal, which the compiler parses to deduce the expected type of the argument list: <code>printf</code> itself has a special type for which you can't build values normally, but <code>sprintf &quot;x=%3&quot;</code> has the type <code>int -&gt; string</code>.</p>
<p>Haskell's type classes (a very fancy overloading mechanism) are powerful enough to define a <a href="https://hackage.haskell.org/package/base-4.18.0.0/docs/Text-Printf.html" rel="nofollow noreferrer"><code>printf</code> function in the base library</a>, but it requires runtime validation of the template against its type. The types of the arguments determine the type class of the template, which determines the implementation of the template-to-string conversion. The template-to-string conversion uses the template string to determine exactly what must be printed, but needs to validate that it matches the expected types for the arguments. For example, <code>printf template 1</code> results in code that knows that it needs to format one integer, and will accept <code>x=%3d</code> as a template but not <code>x=%s</code> or <code>x=%3d, y=%3d</code>.</p>

CC BY-SA 4.0
2023-06-25T07:40:12.093Z
2023-06-30T15:46:52.990Z
2023-06-30T15:46:52.990Z
Gilles 'SO- stop being evil'
CC BY-SA 4.0
2024-09-10T10:31:37.810Z
"All values have runtime type information, so the printing function can use that to determine how to print the argument." -- specifically for Python, since _everything_ are objects and Python fully implements duck-typing, Python's `print()` merely invokes the `__str__()` method of every arg provided. There is no need for `print()` to recognize the actual type of the provided arg.
pepoluan
CC BY-SA 4.0
2024-09-10T11:22:40.370Z
@pepoluan In the case of Python, the runtime type information is the object's class, which includes an `__str__` method. That's not related to duck typing, by the way. It results from a design decision for the Python standard library that all classes know about printing (the alternatives being that printing would have to know about all classes, or that there would be a mechanism to define printing traits independently of the class).
Gilles 'SO- stop being evil'
<p>There is a price you need to pay for <code>print</code> to look like it does in python. Ask yourself if it is an acceptable price that fits well into your balance of goals and compromises.</p>
<p>The price is one of the following:</p>
<ul>
<li>Dynamic type system with a runtime dispatch. With all the consequences - boxed values all over, limited static analysis availability, runtime overhead.</li>
<li>Function/method overloading (like in C++) - you no longer know what is the exact type of function arguments by looking at it. With an IDE with type hints you would not care, but not all languages have IDE integration ready, especially true for the small DSLs / amateur or toy languages. Also, presence of overloading may mess up badly with certain type systems (HM can handle it, but also at some extra cost).</li>
<li>Typed macro metaprogramming (e.g., as in Rust) - in my book, by far the preferable choice, but there is a lot of people with irrational fear of macros, and you're going to alienate them if you bake macros into such a core functionality of your standard library.</li>
</ul>

CC BY-SA 4.0
2023-06-25T11:09:05.433Z
2023-06-25T11:09:05.433Z
SK-logic
CC BY-SA 4.0
2024-12-23T00:07:04.523Z
@kaya3: As Gilles noted, Pascal has `Write` and `WriteLn` functions that behave as a sequence of `WriteXX` calls--one per argument--followed by (in the case of `WriteLn`) a final zero-argument `WriteLn`;.  An interesting extra detail with Pascal is that an argument can include one or two zero-delimited integers to set field width and precision.
supercat
CC BY-SA 4.0
2023-06-25T15:42:07.603Z
There is a fourth option which is special-casing the `print` function in the compiler. So "officially" it can be a varargs function which accepts any sequence of arguments, but the compiler recognises static calls and compiles them the way Rust does. I don't know if any language does this with printing, but Java has something similar to support optimised concatenations with more than operands (see [JEP 280](https://openjdk.org/jeps/280)). Java's approach leaves it to the runtime to generate a concatenation method with the appropriate signature, though.
kaya3
CC BY-SA 4.0
2023-06-25T16:01:03.513Z
@kaya3-supportthestrike - indeed, thanks for reminding.  Although I believe the language should either not allow such things, or allow them to be user-defined.
SK-logic
CC BY-SA 4.0
2023-06-25T18:30:50.473Z
@kaya3-supportthestrike Pascal (or at least many Pascal dialects, I don't remember if that was in the original) has a `write` function that accepts both number and string arguments. You can't define such a function yourself.
Gilles 'SO- stop being evil'
CC BY-SA 4.0
2023-06-26T13:55:39.090Z
Java just accepts an `Object` in its function, and just calls `toString` on it
Seggan
CC BY-SA 4.0
2023-06-26T14:08:04.413Z
@Seggan-OnStrike which is also a polymorphysm, same as function overloading, with all the same potential drawbacks (perceived or real).
SK-logic
<p>That's not C++ style, C++ style looks like this:</p>
<pre><code>std::cout &lt;&lt; 0.5f &lt;&lt; std::endl;
</code></pre>
<p>That said, what you call &quot;C++ style&quot; is actually also available in Python:</p>
<pre><code>string_formatted = '%d %f %s' % (2, 2.0, 'hello')
print(string_formatted)
</code></pre>
<p>The main difference between both styles is that with the values passed directly to the print function it's harder to provide formatting directives, for example with string formatting you can do <code>'%.2f' % (2.0)</code> for <code>'2.0'</code> or <code>'%.5f' % (2.0)</code> for <code>'2.00000'</code>. That's not to say it's impossible, real C++ style with std::cout for example uses modifiers that give state to the output stream for formatting numbers and such:</p>
<pre><code>#include &lt;iostream&gt;
#include &lt;iomanip&gt;

int main()
{
    double d = 122.345;

    std::cout &lt;&lt; std::fixed;
    std::cout &lt;&lt; std::setprecision(2);
    std::cout &lt;&lt; d;
}
</code></pre>
<p>gives you <code>122.34</code>.</p>
<p>As for implementation, both sorts of function need some sort of variable-argument support, but with a caveat. While C-Style string formatting requires you to parse the formatted string, in exchange you get hints on the kinds of arguments you'll be pulling. In C's implementation of variable arguments it's not possible to receive an argument you don't know the type of (you'd need some sort of tagged union), so Python's style of printing function would be very inconvenient to implement.</p>

CC BY-SA 4.0
2023-06-25T01:04:37.453Z
2023-06-25T01:26:10.713Z
2023-06-25T01:26:10.713Z
kouta-kun
# What are the differences between designing a print function Python-style as opposed to C-style?
Posted by: username_0 on 2023-06-24
A regular Python print statement might look like this (Dart printing looks similar to this):
print(a_number) # for a number
print(a_string) # for a string

These print statements can accept arbitrary values without type specification.
In C using printf, a print statement might look like this:
printf("%d", a_number); // for a number
printf("%s\n", a_string.c_str()); // for a string

This one can't, the programmer needs to specify the type.
What are the main differences between designing a print function similar to Python's print() as opposed to C's printf()?
**Tags:** language-design functions language-comparison print
> **Comments:**
> **username_1**: Not my downvotes, but they could be caused by the inappropriate use of "C++" when what is really meant is "C", and by using a non-equivalent Python example instead of `print(f'{a_number}')`.  
> **username_2**: Changing `print(f'{x}')` from `print(x)` significantly changed the question. Those are two entirely separate printing modes  
> **username_3**: After the edit applying Ray's comment, the question has pivoted to more about string formatting than printing.  
> **username_1**: @MichaelHomer, it's not obvious exactly what the question *is* asking about. The two examples illustrate printing where the function determines the type of the arguments and where the caller specifies the type. Is that what the fundamental question is about, the ability to print an arbitrary object without having to specify its type? If so, the answer is that C doesn't do it because it isn't possible to do it. If not, what exactly *is* the characteristic that is meant by "C-style" and "Python-style"?  
> **username_3**: @RayButterworth Yes, there is [a meta post about this question](https://languagedesign.meta.stackexchange.com/questions/401/whats-gone-wrong-with-my-question), in case you haven’t seen it.  
> **username_4**: It's worth noting that C's printf became enough of a security and reliability concern that at least one modern C compiler has the ability to read and check printf format strings at compile time and also to warn about the common "printf(string)" misuse.  
---
## 3 answers
### Answer by username_5 on 2023-06-25. Score: 9 | Accepted answer
It's mainly related to the basics of the type system, but sometimes related to other considerations on the philosophy of the language.

### print with arbitrary values

Some languages have a `print` function that accepts arbitrary values. Others need the program to explicitly convert data to a string. What makes the difference is whether the language makes it possible to infer how to convert the data with some default conversion mechanism (e.g. integers printed in decimal, lists printed as something like the language's list constructor syntax, etc.).

In a dynamically typed language like Python, it's easy. All values have runtime type information, so the printing function can use that to determine how to print the argument.

In statically typed languages, there typically isn't enough information at runtime to find a sensible way to convert a value's representation to a string. For example, a word in memory could contain an integer or a float or a pointer and there's no way to find out which. So any automatic conversion to string has to follow some rules at compile time, where the compiler generates the correct formatting function based on the argument's static type.

C has no such mechanism. C is a low-level language which gives the programmer a lot of control. If you want to print an integer, you have to specify whether you want it in decimal, hexadecimal or octal. If you want to print a floating-point value, you have to specify how many digits of precision. This philosophy is prevalent in C, so the design of the language doesn't include any mechanism for selecting between different implementations of a function based on the argument type. (From C11 onwards there is such a mechanism, but it's restricted to selecting between floating point types for a few built-in functions.)

Many statically typed languages have an overloading mechanism that allows the selection of different implementations of a function based on the argument's type. For example, in C++, the `<<` operator selects between different printing functions based on the type of its right-hand argument, so you can write `cout << 1`, `cout << 1.5`, `cout << "hello"`, etc. (And `<<` even selects between being a printing function and a bitwise operator, based on the type of its left-hand argument.) This is a compile-time mechanism: the compiler knows that there are many `<<` functions, each with their type. In Haskell, this is the [`Show`](https://hackage.haskell.org/package/base/docs/Text-Show.html) [type class](https://en.wikipedia.org/wiki/Type_class), with an instance for each type or type constructor for which a printing mechanism exists.

### printf with a template

Almost all general-purpose languages have function that's similar to C's `sprintf`, often with a similar template syntax. They may or may not have a function that's similar to `printf`, combining the string templating with printing: when combining a string-printing function with a template formatting function is simple enough, there's no need for a function that does both.

For example, in Python, the equivalent of C's `sprintf` is the [`%` operator](https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations), which uses a [printf-like template syntax](https://docs.python.org/3/library/stdtypes.html#old-string-formatting). This templating mechanism [from the original version of the language](http://www.nhn.ou.edu/%7Eatlas/d0/software/python1.5/lib/node11.html) is somewhat deprecated in modern Python in favor of a [different template syntax](https://docs.python.org/3/library/string.html#formatstrings) that is accessible via the [`format` method](https://docs.python.org/3/library/stdtypes.html#str.format) on the template string; the syntax is different, but the core principle is the same. Python only has a `sprintf` equivalent, not a `printf` equivalent, because there wouldn't be much point: it's almost as easy to write `print(template % (arg1, arg2))` or `print(template.format(arg1, arg2))` as it would be to write `print(template, arg1, arg2)`.

Having separate functions requires constructing the resulting string in memory. In C, that's a big deal: you have to allocate enough memory, and C gives you a choice of allocators: you can use a global buffer, or a buffer on the stack, or a buffer allocated by `malloc`, or a buffer allocated by some custom allocator. So it's convenient to have a function that can directly print out the data piece by piece. In a high-level language like Python, the allocation is done entirely under the hood and allocating a string in memory is not considered a big deal.

The main reason some general-purpose don't have a `printf` or `sprintf`-like function is that it's hard to fit into a static type system. You have to match the content of the template string with the types of the other arguments, and even with the number of other arguments. In C, that's not a problem because the type system is very far from sound. It's the responsibility of the programmer to pass the correct number of arguments with the correct types. In dynamically typed languages, that's not a problem because the templating function can check argument types at runtime.

Many statically typed languages arrange some printf-like mechanism anyway, with various degree of “compiler magic”, i.e. you couldn't implement `printf` as a library function. For example, [Pascal](https://en.wikibooks.org/wiki/Pascal_Programming/Input_and_Output#Formatting_output) has special handling for some functions like `write` which accept multiple argument types and even a field width formatting syntax. You can write `write('x=', x:3)` but you can't make your own function that accepts a variable number of arguments, or that takes the extra width annotation `:3`. In Ocaml, the [`printf` mechanism](https://en.wikibooks.org/wiki/Pascal_Programming/Input_and_Output#Formatting_output) requires the template to be a string literal, which the compiler parses to deduce the expected type of the argument list: `printf` itself has a special type for which you can't build values normally, but `sprintf "x=%3"` has the type `int -> string`.

Haskell's type classes (a very fancy overloading mechanism) are powerful enough to define a [`printf` function in the base library](https://hackage.haskell.org/package/base-4.18.0.0/docs/Text-Printf.html), but it requires runtime validation of the template against its type. The types of the arguments determine the type class of the template, which determines the implementation of the template-to-string conversion. The template-to-string conversion uses the template string to determine exactly what must be printed, but needs to validate that it matches the expected types for the arguments. For example, `printf template 1` results in code that knows that it needs to format one integer, and will accept `x=%3d` as a template but not `x=%s` or `x=%3d, y=%3d`.
> **Comments:**
> **username_6** (2024-09-10): "All values have runtime type information, so the printing function can use that to determine how to print the argument." -- specifically for Python, since _everything_ are objects and Python fully implements duck-typing, Python's `print()` merely invokes the `__str__()` method of every arg provided. There is no need for `print()` to recognize the actual type of the provided arg.  
> **username_5** (2024-09-10): @pepoluan In the case of Python, the runtime type information is the object's class, which includes an `__str__` method. That's not related to duck typing, by the way. It results from a design decision for the Python standard library that all classes know about printing (the alternatives being that printing would have to know about all classes, or that there would be a mechanism to define printing traits independently of the class).  
---
### Answer by username_7 on 2023-06-25. Score: 6
There is a price you need to pay for `print` to look like it does in python. Ask yourself if it is an acceptable price that fits well into your balance of goals and compromises.

The price is one of the following:

* Dynamic type system with a runtime dispatch. With all the consequences - boxed values all over, limited static analysis availability, runtime overhead.
* Function/method overloading (like in C++) - you no longer know what is the exact type of function arguments by looking at it. With an IDE with type hints you would not care, but not all languages have IDE integration ready, especially true for the small DSLs / amateur or toy languages. Also, presence of overloading may mess up badly with certain type systems (HM can handle it, but also at some extra cost).
* Typed macro metaprogramming (e.g., as in Rust) - in my book, by far the preferable choice, but there is a lot of people with irrational fear of macros, and you're going to alienate them if you bake macros into such a core functionality of your standard library.
> **Comments:**
> **username_9** (2023-06-25): There is a fourth option which is special-casing the `print` function in the compiler. So "officially" it can be a varargs function which accepts any sequence of arguments, but the compiler recognises static calls and compiles them the way Rust does. I don't know if any language does this with printing, but Java has something similar to support optimised concatenations with more than operands (see [JEP 280](https://openjdk.org/jeps/280)). Java's approach leaves it to the runtime to generate a concatenation method with the appropriate signature, though.  
> **username_7** (2023-06-25): @kaya3-supportthestrike - indeed, thanks for reminding.  Although I believe the language should either not allow such things, or allow them to be user-defined.  
> **username_5** (2023-06-25): @kaya3-supportthestrike Pascal (or at least many Pascal dialects, I don't remember if that was in the original) has a `write` function that accepts both number and string arguments. You can't define such a function yourself.  
> **username_10** (2023-06-26): Java just accepts an `Object` in its function, and just calls `toString` on it  
> **username_7** (2023-06-26): @Seggan-OnStrike which is also a polymorphysm, same as function overloading, with all the same potential drawbacks (perceived or real).  
> **username_8** (2024-12-23): @kaya3: As Gilles noted, Pascal has `Write` and `WriteLn` functions that behave as a sequence of `WriteXX` calls--one per argument--followed by (in the case of `WriteLn`) a final zero-argument `WriteLn`;.  An interesting extra detail with Pascal is that an argument can include one or two zero-delimited integers to set field width and precision.  
---
### Answer by username_11 on 2023-06-25. Score: 4
That's not C++ style, C++ style looks like this:
std::cout << 0.5f << std::endl;

That said, what you call "C++ style" is actually also available in Python:
string_formatted = '%d %f %s' % (2, 2.0, 'hello')
print(string_formatted)

The main difference between both styles is that with the values passed directly to the print function it's harder to provide formatting directives, for example with string formatting you can do '%.2f' % (2.0) for '2.0' or '%.5f' % (2.0) for '2.00000'. That's not to say it's impossible, real C++ style with std::cout for example uses modifiers that give state to the output stream for formatting numbers and such:
#include 
#include 

int main()
{
    double d = 122.345;

    std::cout << std::fixed;
    std::cout << std::setprecision(2);
    std::cout << d;
}

gives you 122.34.
As for implementation, both sorts of function need some sort of variable-argument support, but with a caveat. While C-Style string formatting requires you to parse the formatted string, in exchange you get hints on the kinds of arguments you'll be pulling. In C's implementation of variable arguments it's not possible to receive an argument you don't know the type of (you'd need some sort of tagged union), so Python's style of printing function would be very inconvenient to implement.
---
